# Number format

> Locale-aware number helpers: cached Intl formatting, parsing what people type, step math and currency affixes.

- Type: React helper (Hooks & Utilities)
- Access: Free (the source is open to everyone)
- Version: 1.0.0
- Install: `npx wingo-ui@latest add number-format`
- Docs: https://wingo-ui.com/components/number-format

## Interaction

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.

## Installation

```bash
npx wingo-ui@latest add number-format
```

The CLI adds the files, their dependencies and a version record for updates.

With shadcn: `npx shadcn@latest add https://wingo-ui.com/r/number-format.json`

Files:

- `lib/number-format.ts`

## Usage

```tsx
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

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `locale?` | `string` | `"en-US"` | The demo's locale. Every helper takes an optional locale; pass the one from useLocale so server and browser agree. |
| `currency?` | `string` | `"USD"` | An ISO 4217 code for getCurrencyParts and currency formatting. |
| `maxDecimals?` | `number` | `2` | formatEditable's maxDecimals: decimals kept while typing; extra ones are cut, not rounded. |
| `parseNumber?` | `(text: string, locale?: string) => number \| null` |  | What 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 (function)

Intl 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 (function)

Intl formatting with a cached formatter. formatNumber returns "" for null and NaN so an empty field stays empty. Signature: (locale?, options?) => Intl.NumberFormat.

### getNumberSeparators (function)

The 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 (function)

How 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 (function)

Re-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 (function)

Keep 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 (function)

Keep 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 (function)

Step 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 (function)

Step 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 (function)

Step 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 (function)

Step 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.

## See also

- [Text match](https://wingo-ui.com/components/text-match) ([Markdown](https://wingo-ui.com/components/text-match.md))
- [Date utils](https://wingo-ui.com/components/date-utils) ([Markdown](https://wingo-ui.com/components/date-utils.md))
- [useLocale](https://wingo-ui.com/components/use-locale) ([Markdown](https://wingo-ui.com/components/use-locale.md))

---

Canonical: https://wingo-ui.com/components/number-format
More components: https://wingo-ui.com/components · Index for agents: https://wingo-ui.com/llms.txt
