<!-- Protocore Design System — NumberFormatter -->
# NumberFormatter

- **Category:** Data Display (`data-display`)
- **Slug:** `data-display/number-formatter`
- **Status:** stable
- **Platforms:** web
- **Import:** `import { NumberFormatter } from "@protocore/pds";`
- **Docs:** https://pds.protocore.io/components/data-display/number-formatter

> A thin Intl.NumberFormat wrapper for decimals, currency, percent, and units, in mono tabular figures.

## When to use it

**NumberFormatter** is the general-purpose number renderer: counts, ratios, measurements, quantities. It forwards its props straight to `Intl.NumberFormat` with an explicit default `locale` (`"en"`) so a server render and its hydration are byte-identical.

For money specifically, reach for **MoneyAmount** — it adds minor-unit awareness and sign coloring on top of the same Intl core. A non-finite input renders the literal value with a `data-invalid` marker rather than a fabricated number.

## Props

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `className` | `string` | no | — | — |
| `currency` | `string` | no | — | ISO 4217 code — required when `style="currency"` (e.g. `"EUR"`). |
| `locale` | `string` | no | `en` | BCP-47 locale for grouping / decimal separators and unit names. Fixed and explicit so SSR and hydration agree. |
| `maximumFractionDigits` | `number` | no | — | Maximum fractional digits. |
| `minimumFractionDigits` | `number` | no | — | Minimum fractional digits. |
| `notation` | `enum` | no | `standard` | Notation: `"standard"` or `"compact"` (`1200 → 1.2K`). |
| `signDisplay` | `enum` | no | `auto` | Sign policy passed through to Intl. |
| `style` | `enum` | no | `decimal` | Formatting style. |
| `unit` | `string` | no | — | Unit identifier — required when `style="unit"` (e.g. `"kilometer"`). |
| `value` | `string \| number` | yes | — | The number to format. A numeric string off the wire is coerced. |

## Examples

### Styles

`style` selects the Intl formatter: `"decimal"` (default, with grouping), `"currency"` (pair with `currency`), `"percent"`, or `"unit"` (pair with `unit`). Figures render mono and tabular so columns line up.

```tsx
import { NumberFormatter } from "@protocore/pds";

export default function NumberFormatterStyles() {
  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 8, alignItems: "flex-start" }}>
      <NumberFormatter value={1234567} />
      <NumberFormatter value={1299.5} style="currency" currency="EUR" />
      <NumberFormatter value={0.428} style="percent" maximumFractionDigits={1} />
      <NumberFormatter value={42} style="unit" unit="kilometer" />
    </div>
  );
}
```

### Compact notation

`notation="compact"` renders `1200000` as `1.2M` — for dense dashboards and stat tiles where the exact figure lives in a tooltip.

```tsx
import { NumberFormatter } from "@protocore/pds";

export default function NumberFormatterCompact() {
  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 8, alignItems: "flex-start" }}>
      <NumberFormatter value={1200} notation="compact" />
      <NumberFormatter value={1200000} notation="compact" />
      <NumberFormatter value={3_400_000_000} notation="compact" />
    </div>
  );
}
```

## Usage

**Do**

- Pass a raw number (or numeric string) and let Intl format it.
- Set `currency` with `style="currency"`, `unit` with `style="unit"`.
- Use `notation="compact"` for stat tiles and dense dashboards.
- Keep a fixed `locale` so SSR and client agree.

**Don't**

- Pre-format the number into a string; that can't localize or align.
- Use it for money with minor-units — reach for MoneyAmount.
- Rely on the runtime default locale; set it explicitly.

## Accessibility

**Notes**

- Renders a plain `<span>`; the formatted string is the accessible text.
- Mono tabular figures keep columns of numbers vertically aligned.

## Related

`money-amount`, `format-byte`, `metric-delta`, `rolling-number`

---

© Protocore. All rights reserved. Use of the Protocore Design System requires prior written authorization from Protocore (contact@protocore.io). These machine-readable materials must not be ingested into ML-training datasets or derivative design systems. See https://pds.protocore.io/legal/
