Number format TypeScript helper for React
v1.0.0Added in 1.0.0
Locale-aware number helpers: cached Intl formatting, parsing what people type, step math and currency affixes.
Type an amount: the field groups thousands as you type in the chosen locale and the caret never jumps. A dot or a comma both work as the decimal point, like on a phone keypad. The rows below show the parsed number, the same value formatted three ways and how the locale writes the currency.
Number format is a free React helper in the React hooks and utilities category, written in TypeScript for React 19. It installs as source with npx wingo-ui@latest add number-format.
Add to a project
Requirements: React 19, in Next.js (App Router or Pages), Vite or React Router. Installation guide · Next.js setup · Vite setup
npx wingo-ui@latest add number-formatUsage
Import parseNumber from @/lib/number-format.
import { formatNumber, getCurrencyParts, parseNumber } from "@/lib/number-format"
const price = parseNumber("1,234.5", "en-US") // 1234.5
parseNumber("1.234,5", "de-DE") // 1234.5
formatNumber(price, "en-US", { style: "currency", currency: "USD", currencyDisplay: "narrowSymbol" }) // "$1,234.50"
getCurrencyParts("USD", "en-US") // { symbol: "$", position: "prefix", spaced: false, fractionDigits: 2 }
getCurrencyParts("RON", "ro-RO") // { symbol: "lei", position: "suffix", spaced: true, fractionDigits: 2 }API
localedefault"en-US"stringThe demo's locale. Every helper takes an optional locale; pass the one from useLocale so server and browser agree.
currencydefault"USD"stringAn ISO 4217 code for getCurrencyParts and currency formatting.
maxDecimalsdefault2numberformatEditable's maxDecimals: decimals kept while typing; extra ones are cut, not rounded.
parseNumber(text: string, locale?: string) => number | nullWhat someone typed, as a number (null when empty or not a number). The locale's decimal separator wins; without it a lone "." or "," before exactly three digits groups thousands ("1.234" = 1234 in de-DE) and anything else is a decimal point ("12.5" = 12.5). Symbols, units, spaces and "%" are ignored; the unicode minus works.
Parts
formatNumber
functionIntl formatting with a cached formatter. formatNumber returns "" for null and NaN so an empty field stays empty. Signature: (value: number | null | undefined, locale?: string, options?: Intl.NumberFormatOptions) => string.
getNumberFormatter
functionIntl formatting with a cached formatter. formatNumber returns "" for null and NaN so an empty field stays empty. Signature: (locale?, options?) => Intl.NumberFormat.
getNumberSeparators
functionThe locale's characters: en-US "," and ".", de-DE "." and ",", fr-FR a narrow space and ",". Signature: (locale?: string) => { group: string; decimal: string; minus: string }.
getCurrencyParts
functionHow a locale writes a currency, for a field's prefix or suffix: USD in en-US is "$" before the number, RON in ro-RO is "lei" after it with a space. display defaults to narrowSymbol ("$", "€", "lei"); Intl's own default would print "RON". Signature: (currency: string, locale?: string, display?: "symbol" | "narrowSymbol" | "code" | "name") => { symbol: string; position: "prefix" | "suffix"; spaced: boolean; fractionDigits: number }.
formatEditable
functionRe-formats a field's text while someone types: groups the integer part, keeps a trailing decimal separator ("12,") and trailing zeros ("12,50"), turns a typed "." into the locale decimal on an english keypad. Signature: (text: string, locale?: string, options?: { maxDecimals?: number; grouping?: boolean; allowNegative?: boolean }) => string.
digitsBeforeCaret
functionKeep the caret in place across a re-format: count the digits (and the decimal separator) before it, re-format, then put it back after the same count. Signature: (text: string, caret: number, locale?: string) => number.
caretAfterDigits
functionKeep the caret in place across a re-format: count the digits (and the decimal separator) before it, re-format, then put it back after the same count. Signature: (text: string, count: number, locale?: string) => number.
clamp
functionStep math without float noise: roundTo(0.1 + 0.2, 2) is 0.3, snapToStep(0.35, 0.1) is 0.4, decimalsOf(0.05) is 2 (the precision a step implies). Signature: (value, min?, max?).
roundTo
functionStep math without float noise: roundTo(0.1 + 0.2, 2) is 0.3, snapToStep(0.35, 0.1) is 0.4, decimalsOf(0.05) is 2 (the precision a step implies). Signature: (value, decimals).
snapToStep
functionStep math without float noise: roundTo(0.1 + 0.2, 2) is 0.3, snapToStep(0.35, 0.1) is 0.4, decimalsOf(0.05) is 2 (the precision a step implies). Signature: (value, step, origin?).
decimalsOf
functionStep math without float noise: roundTo(0.1 + 0.2, 2) is 0.3, snapToStep(0.35, 0.1) is 0.4, decimalsOf(0.05) is 2 (the precision a step implies). Signature: (value) => number.
Examples
One amount, six locales
formatNumber with style currency prints the same euro amount with each locale's separators and symbol position.
Parsing pasted text
parseNumber reads amounts the way people type them in each locale and returns null for anything that is not a number.
Currency parts
getCurrencyParts tells a money input where the symbol goes and how many decimals the currency uses.
Dependencies
No dependencies beyond React.
Files
- lib/number-format.ts
FAQ
Is the Number format helper free?
Yes. The source is free and needs no account: copy it from the Code tab or install it with the CLI.
How do I install Number format?
Run npx wingo-ui@latest add number-format. With shadcn, run npx shadcn@latest add https://wingo-ui.com/r/number-format.json.
See also
Used in
More React hooks and utilities
Articles using this component
- React Currency Input: Intl Formatting Without Caret Jumps
Component guides10 min read