NumberFormat
$ npx shadcn@latest add https://lacodda.github.io/dowel/r/number-format.jsonThe separators are the language’s. A thousand is 1,000 here, 1 000 there
and 1.000 somewhere else — and the last one is the same string another reader
would read as one. Intl knows this and a product does not have to.
The figures line up. tabular-nums gives every digit the same width, so a
column of numbers has its digits above each other and the eye can compare
lengths without reading. Without it a proportional font gives 1 less room
than 8, the column ripples, and the only way to tell 9,999 from 10,000 is to
count.
This is the part that gets left out, because it looks fine on the one number a developer tries it on and only fails in a column — which is exactly where numbers live. Having it in a component is how it stops being remembered.
<NumberFormat value={1234567} /><NumberFormat value={1299.5} style="currency" currency="USD" /><NumberFormat value={0.427} style="percent" maximumFractionDigits={1} /><NumberFormat value={1200000} notation="compact" /> {/* 1.2M */}The language is the application’s, not the browser’s. Left unset, the
locale is useLocale()’s answer — the nearest LocaleProvider, else the
page’s <html lang>. It used to be the browser’s language, which is how
kasl-server showed an English interface with Russian week headings to a reader
whose browser spoke Russian. Passing 'en-US' to be safe is the opposite
mistake: a German reader shown American separators for the life of the
product. See no-implicit-locale.
Intl’s options, not a second vocabulary. Currency, percentages and units
take the same options object, so those are not three more components. What
Intl does not decide is compactness: 1.2M is a choice about how much
precision the reader is owed, and it is the caller’s, in notation.
style means Intl’s style here, not a CSS object — the two collide on
the name, and the formatting one wins. Inline styles are given up in exchange,
which costs nothing: appearance is the theme’s business and className is
still there.
value is a number, not number | null. A row with no value shows
whatever the product says absence looks like — a dash, a word, an empty cell —
and that is a decision about the data, not about formatting. See
table-sort for the same distinction in the
ordering.
formatNumber(value, locale, options?) gives the string alone, for a title,
an aria-label or a CSV.
Props
Read from the types in ui/number-format.tsx, the file a product copies - so the description of each prop is also what an editor shows over it there.
| Prop | Type | Default | |
|---|---|---|---|
valuerequired | number | The number to format. | |
locale | string | The application's language by default - see useLocale. |
And every prop of React's HTMLAttributes<HTMLSpanElement> except children, style and Intl.NumberFormatOptions, passed on.
Accessibility
The examples on the stand, through axe 4.14.0 in a browser, in both themes, against WCAG 2.2 A and AA and axe's best practices: 4 rules applied and passed, and none failed. The accessibility report has every item.
History
Each release that changed ui/number-format.tsx - the file a product copies - newest first.