@dnb/eufemia
Version:
DNB Eufemia Design System UI Library
640 lines (508 loc) • 21.2 kB
Markdown
---
title: 'NumberFormat'
description: 'A ready to use DNB number formatter.'
version: 10.104.0
generatedAt: 2026-04-17T18:46:09.919Z
checksum: 090b7d977ba4be5e2c4c04d199a30a4048416c59f443a56985df2f80629d9c40
---
# NumberFormat
## Import
```tsx
import { NumberFormat } from '@dnb/eufemia'
```
## Description
A ready-to-use DNB number formatter. Use it wherever you have to display a number, a currency value, a phone number, etc.
For a complete locale comparison, see [Best Practices for number formatting](/uilib/usage/best-practices/for-formatting/).
## Relevant links
- [Figma](https://www.figma.com/design/cdtwQD8IJ7pTeE45U148r1/%F0%9F%92%BB-Eufemia---Web?node-id=52173-680)
- [Source code](https://github.com/dnbexperience/eufemia/tree/main/packages/dnb-eufemia/src/components/number-format)
- [Docs code](https://github.com/dnbexperience/eufemia/tree/main/packages/dnb-design-system-portal/src/docs/uilib/components/number-format)
Good reasons for why we have this:
- To standardize the formatting of numbers for all DNB applications.
- To make numbers accessible to screen readers.
### Supported formats
- Numbers in general e.g. <code className="dnb-code"><NumberFormat value="12345678.90" /></code>
- Currency e.g. <code className="dnb-code"><NumberFormat currency value="12345678.90" /></code>
- Percentage e.g. <code className="dnb-code"><NumberFormat percent value="12.34" /></code>
- Phone numbers e.g. <code className="dnb-code"><NumberFormat phone value="004799999999" /></code>
- Bank account number e.g. <code className="dnb-code"><NumberFormat ban value="20001234567" /></code>
- National identification number e.g. <code className="dnb-code"><NumberFormat nin value="18089212345" /></code>
- Organization number e.g. <code className="dnb-code"><NumberFormat org value="123456789" /></code>
- Compact (short) numbers e.g. <code className="dnb-code"><NumberFormat compact value="12345678" decimals={1} /></code>
- Compact (long) currency e.g. <code className="dnb-code"><NumberFormat compact="long" currency currency_display="name" value="12345678" decimals={1} /></code>
### Defaults
It uses the browser APIs `number.toLocaleString` or `Intl.NumberFormat.format` under the hood. As well as some custom formatter. The locale defaults to:
- Locale: `nb-NO`
- Currency: `NOK`
#### Norwegian kroner
When the currency format is set to `currency_display="name"`, the currency will be displayed as "kroner" instead of "Norwegian kroner".
- Norwegian currency: <code className="dnb-code"><NumberFormat currency currency_display="name" value="1234.90" /></code>
- Swedish currency: <code className="dnb-code"><NumberFormat currency="SEK" currency_display="name" value="1234.90" /></code>
#### Not available
When a number should be displayed but is not available to the frontend application, the NumberFormat component will display a single **em dash** (–), and a screen reader will receive the text "Ikke tilgjengelig" / "Not available".
Example: <NumberFormat value="invalid" currency />
## Decimals
If the value has more decimal places than specified by the `decimals={2}` property, it will be rounded accordingly.
Here are the available options for the `rounding` property:
- `omit`: Truncate decimals without rounding.
- `half-even`: Round to the nearest even number.
- `half-up` (default): Round up if the fractional part is 0.5 or greater; otherwise, round down.
## Handling en-NO
`en-NO` (English – Norway) is a valid BCP 47 locale and is commonly sent by devices configured with English language and Norway region.
If region-aware formatting is supported, accept en-NO and use it for date, time, and number formatting.
If only specific English locales are supported (e.g. en-GB), explicitly map en-NO to the closest supported locale.
Locale handling must be explicit and consistent across the application.
## Value Components
The formatting helpers power several `Value.*` components:
- [Value.Number](/uilib/extensions/forms/Value/Number/)
- [Value.Currency](/uilib/extensions/forms/Value/Currency/)
- [Value.Date](/uilib/extensions/forms/Value/Date/)
- [Value.DateOfBirth](/uilib/extensions/forms/Value/DateOfBirth/)
- [Value.PhoneNumber](/uilib/extensions/forms/Value/PhoneNumber/)
- [Value.BankAccountNumber](/uilib/extensions/forms/Value/BankAccountNumber/)
- [Value.NationalIdentityNumber](/uilib/extensions/forms/Value/NationalIdentityNumber/)
- [Value.OrganizationNumber](/uilib/extensions/forms/Value/OrganizationNumber/)
## Provider
You can send down the `locale` as an application-wide property (Context). More info about the [provider and locale usage](/uilib/components/number-format/provider).
```jsx
import Provider from '@dnb/eufemia/shared/Provider'
render(
<Provider locale="en-GB" NumberFormat={{ currency_display: 'code' }}>
<MyApp>
text <NumberFormat>123</NumberFormat> table etc.
</MyApp>
</Provider>
)
```
## NumberFormat Hook
**Heads up:** If you do so, keep in mind that you will have to ensure all the accessibility enhancements the component offers. For that, you can use the `aria` field:
```jsx
import Provider from '@dnb/eufemia/shared/Provider'
import { useNumberFormat } from '@dnb/eufemia/components/useNumberFormat'
function Component() {
// By using returnAria you get an object
const { number, aria } = useNumberFormat(12345678.9, {
// Props are inherited from the Eufemia Provider and the NumberFormat object
returnAria: true,
})
return (
<span>
<span aria-hidden>{number}</span>
<span className="dnb-sr-only">{aria}</span>
</span>
)
}
render(
<Provider locale="en-GB" NumberFormat={{ currency: 'EUR' }}>
<Component />
</Provider>
)
```
### NumberFormat Hook with parts
You can also use `useNumberFormatWithParts` when you need split output for custom layouts:
```jsx
import Provider from '@dnb/eufemia/shared/Provider'
import { useNumberFormatWithParts } from '@dnb/eufemia/components/NumberFormat'
function Component() {
// useNumberFormatWithParts defaults to returnAria=true
const { number, aria, parts } = useNumberFormatWithParts(12345678.9, {
currency: true,
})
return (
<span>
<span aria-hidden>
{parts.sign}
{parts.number}
{parts.currency ? ` ${parts.currency}` : null}
</span>
<span className="dnb-sr-only">{aria}</span>
</span>
)
}
render(
<Provider locale="en-GB" NumberFormat={{ currency: 'EUR' }}>
<Component />
</Provider>
)
```
## Related component
For prominent values with dedicated typography controls, use [Stat](/uilib/components/stat/).
## Formatting only (interceptor)
You can use the `format` method without using a React Component or React Hook.
**Heads up:** If you do so, keep in mind that you will have to ensure all the accessibility enhancements the component offers. For that, you can use the `aria` field:
```ts
import { format } from '@dnb/eufemia/components/number-format/NumberUtils'
// By using returnAria you get an object
const { number, aria } = format(12345678.9, {
locale: 'nb-NO', // not inherited
currency: true,
returnAria: true,
})
// Basic formatting
const number = format(1234)
```
The `format` method will accept the same [properties](/uilib/components/number-format/properties) as the component.
### Interceptor helpers
Also, you may check out the related tests **NumberFormat > cleanNumber** in the source code to find more examples.
```ts
import { cleanNumber } from '@dnb/eufemia/components/number-format/NumberUtils'
const string = cleanNumber('prefix -12 345,678 suffix') // returns -12345.678
const string = cleanNumber('prefix -12.345,678 suffix') // returns -12345.678
```
### Element and style
The number component is style-independent, so it has no visual styles. By default, a `<span>` is used (with [speak-as: numbers](https://developer.mozilla.org/en-US/docs/Web/CSS/@counter-style/speak-as), even though the support is very low). However, you can easily change the element type by providing a different value to the `element="div"` property.
## Accessibility
**NVDA** also has [issues](https://github.com/nvaccess/nvda/issues/8874) reconciling the `lang` attribute, which makes it hard to have a solid and good solution for reading numbers. VoiceOver on desktop does a perfect job with this.
**VoiceOver** on mobile devices (iOS) only supports numbers read out properly to a maximum of `99,999.00`. On amounts above this value, VO reads numbers digit by digit.
To enhance the **Copy & Paste** experience of copying numbers into other applications (Excel), you may use the `clean_copy_value` property. It will then provide a second number without thousand separators and with a comma/dot (depending on the locale) as the decimal separator. This number is not visible but will be used when selecting and copying the whole number on the first click to the system clipboard.
You can enable this feature on all your NumberFormat components by using the `Provider`:
```jsx
import { Provider } from '@dnb/eufemia/shared'
render(
<Provider value={{ NumberFormat: { clean_copy_value: true } }}>
<YourApp />
</Provider>
)
```
### More details
> Screen readers require numbers to be formatted properly in order to be read as numbers. The **NumberFormat** component helps achieve this requirement.
Numbers are formatted differently for screen readers than the visual number. Numbers also get assigned a `lang` attribute so the screen reader knows what language (locale) should be used for the particular number, even if the surrounding text does not correspond to the same language.
### Sources
Eufemia bases its number formats on both the [Norwegian authority](https://lovdata.no/forskrift/2004-02-16-426/§16) and [Språkradet](https://www.sprakradet.no/sprakhjelp/Skriveregler/Dato), and currency is based on [guidelines](https://www.sprakradet.no/svardatabase/sporsmal-og-svar/kronebelop-rekkjefolgje-komma-og-strek/) from Språkrådet. Wikipedia has more info on worldwide [decimal separator](https://en.wikipedia.org/wiki/Decimal_separator) usage.
For international number formatting we use these sources:
- [ONS – Writing numbers](https://service-manual.ons.gov.uk/content/numbers/writing-numbers)
- [GOV.UK Style Guide](https://www.gov.uk/guidance/style-guide/a-to-z-of-gov-uk-style#numbers)
- [NHS Service Manual](https://service-manual.nhs.uk/content/numbers-measurements-dates-time) all recommend using **commas** as the thousands separator for `en-GB`.
- [EU Data Visualisation Guide](https://data.europa.eu/apps/data-visualisation-guide/number-formatting) states the same for digital content.
- [EU’s Handbook for authors and translators](https://commission.europa.eu/system/files/2023-11/styleguide_english_dgt_en.pdf) specifies non-breaking spaces as the main rule, **but explicitly allows commas when writing for the web** – which is exactly our context.
- [ISO 80000-1](https://www.iso.org/standard/76921.html) (formerly ISO 31-0) is a scientific/technical standard and **not a linguistic style guide**, so we do not use it for how we present numbers to end users on the web.
**Difference between formats:**
- `1 234.00` – 🇬🇧 Current DNB practice for English
- `1,234.00` – 🇬🇧 Recommended by official UK sources (ONS, GOV.UK, NHS)
**Accessibility:** WCAG [1.3.1 Info and Relationships](https://www.w3.org/WAI/WCAG21/Understanding/info-and-relationships.html) and [3.1.1 Language of Page](https://www.w3.org/WAI/WCAG21/Understanding/language-of-page.html) require that content can be correctly interpreted by assistive technologies.
When using spaces as thousand separators, screen readers misinterpret numbers in English. For example: `45 804` is read as "45" and "804" instead of "forty-five thousand eight hundred and four."
## Node.js and SSR usage
If you run the component or `format` function in [Node.js](https://nodejs.org), you have to include [ICU](https://nodejs.org/api/intl.html) data in order to display other locales than en-GB. You can do this by:
- installing `npm i full-icu`
- and call node (or jest) with an environment variable pointing to the package: `NODE_ICU_DATA=./node_modules/full-icu node ...`
- after a Node.js version upgrade you may have to run `npm rebuild`
## Known issues
Edge Browser on Windows 10 is converting numbers automatically to followable links. This makes the experience on NVDA bad, as it reads also the new, unformatted link number.
You can [disable this behavior](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/x-ms-format-detection):
```html
<html x-ms-format-detection="none">
...
</html>
```
## Demos
<ChangeLocale
label="Locale used in the demos:"
label_direction="vertical"
/>
### Default numbers
```tsx
render(
<Style>
<ComponentBox data-visual-test="number-format-default">
<P>
<NumberFormat value="12345" srLabel="Total:" />
<NumberFormat>-12345678.9</NumberFormat>
<NumberFormat prefix={<b>prefix</b>} suffix="suffix">
-12345678.9
</NumberFormat>
<NumberFormat decimals={1}>-1234.54321</NumberFormat>
<NumberFormat decimals={2} copy_selection={false}>
-1234
</NumberFormat>
<NumberFormat decimals={2}>invalid</NumberFormat>
</P>
</ComponentBox>
</Style>
)
```
### Currency
```tsx
render(
<Style>
<ComponentBox data-visual-test="number-format-currency">
<P>
<NumberFormat currency>12345</NumberFormat>
<NumberFormat
currency
currency_position="before"
value={-12345678.9}
/>
<NumberFormat currency value={-12345678.95} decimals={0} />
<NumberFormat
currency
value={-12345678.9}
currency_display="code"
/>
<NumberFormat
currency
value={-12345678.9}
currency_display={false}
/>
<NumberFormat currency decimals={2}>
invalid
</NumberFormat>
</P>
</ComponentBox>
</Style>
)
```
### Hero-style values
For prominent values, use [Stat](/uilib/components/stat/) with `Stat.Currency` and `Stat.Percent`.
```tsx
render(
<Style>
<ComponentBox>
<Stat.Currency
value={12345}
currency="NOK"
suffix="/mnd"
signDisplay="always"
mainSize="x-large"
auxiliarySize="x-small"
/>
</ComponentBox>
</Style>
)
```
### Compact (shorten) numbers
Shorten numbers should only be used for numbers above 100 000. A small `k` for thousand is not a Norwegian standard, and should not be used in formal contexts.
```tsx
render(
<Style>
<ComponentBox data-visual-test="number-format-compact">
<P>
<NumberFormat compact decimals={1}>
1234
</NumberFormat>
<NumberFormat compact decimals={1} value={123456} />
<NumberFormat compact="short" decimals={2} value={-1723967.38} />
<NumberFormat compact="long" decimals={3} value={-1234567.9876} />
<NumberFormat
compact="long"
currency
value={12345}
decimals={1}
currency_display="name"
/>
<NumberFormat compact value={123455678912} decimals={3} />
</P>
</ComponentBox>
</Style>
)
```
### Percentage
```tsx
render(
<Style>
<ComponentBox data-visual-test="number-format-percent">
<P>
<NumberFormat percent value="12.34" />
<NumberFormat percent>-12.34</NumberFormat>
<NumberFormat percent decimals={1}>
-12.34
</NumberFormat>
</P>
</ComponentBox>
</Style>
)
```
### Phone
By using `selectall={false}` you disable the auto-select all feature.
```tsx
render(
<Style>
<ComponentBox data-visual-test="number-format-phone">
<P>
<NumberFormat value="99999999" phone />
<NumberFormat value="4799999999" phone />
<NumberFormat value="004799999999" phone />
<NumberFormat value="+4780022222" phone link="sms" />
<NumberFormat value="+47116000" phone selectall={false} />
<NumberFormat value="+4702000" phone />
</P>
</ComponentBox>
</Style>
)
```
### Bank Account number (Kontonummer)
```tsx
render(
<Style>
<ComponentBox data-visual-test="number-format-ban">
<P>
<NumberFormat value="20001234567" ban />
</P>
</ComponentBox>
</Style>
)
```
### National Identification number (Fødselsnummer)
```tsx
render(
<Style>
<ComponentBox data-visual-test="number-format-nin">
<P>
<NumberFormat value="18089212345" nin />
</P>
</ComponentBox>
</Style>
)
```
### Organization number (Organisasjonsnummer)
```tsx
render(
<Style>
<ComponentBox data-visual-test="number-format-org">
<P>
<NumberFormat value="123456789" org suffix="MVA" />
</P>
</ComponentBox>
</Style>
)
```
### Numbers and currencies in different locales
```tsx
render(
<Style>
<ComponentBox data-visual-test="number-format-locales">
<H3>Numbers</H3>
<P>
<NumberFormat locale="nb-NO" value="-12345678.9" />
<NumberFormat locale="en-GB" value="-12345678.9" />
<NumberFormat locale="de-DE" value="-12345678.9" />
<NumberFormat locale="de-CH" value="-12345678.9" />
<NumberFormat locale="fr-CH" value="-12345678.9" />
</P>
<H3>Currencies</H3>
<P>
<NumberFormat locale="nb-NO" value="-12345.6" currency />
<NumberFormat locale="en-GB" value="-12345.6" currency />
<NumberFormat locale="de-DE" value="-12345.6" currency />
<NumberFormat locale="de-CH" value="-12345.6" currency />
<NumberFormat locale="fr-CH" value="-12345.6" currency />
</P>
</ComponentBox>
</Style>
)
```
### NumberFormat and spacing
The NumberFormat uses `display: inline-block;` in order to make the [spacing system](/uilib/layout/space) to work.
```tsx
render(
<Style>
<ComponentBox data-visual-test="number-format-spacing">
<span>text</span> <NumberFormat value="1234" currency left right />
<span>text</span> <NumberFormat value="5678" currency left right />
<span>text</span>
</ComponentBox>
</Style>
)
```
### Sign display
Control when to display the sign for numbers using the `signDisplay` property. Options include `always`, `exceptZero`, `negative`, and `never`.
```tsx
render(
<Style>
<ComponentBox data-visual-test="number-format-sign-display">
<H3>signDisplay="auto"</H3>
<P>
<NumberFormat signDisplay="auto" value={1234} />
<NumberFormat signDisplay="auto" value={-1234} />
<NumberFormat signDisplay="auto" value={0} />
</P>
<H3>signDisplay="always"</H3>
<P>
<NumberFormat signDisplay="always" value={1234} />
<NumberFormat signDisplay="always" value={-1234} />
<NumberFormat signDisplay="always" value={0} />
</P>
<H3>signDisplay="never"</H3>
<P>
<NumberFormat signDisplay="never" value={1234} />
<NumberFormat signDisplay="never" value={-1234} />
<NumberFormat signDisplay="never" value={0} />
</P>
<H3>signDisplay="negative"</H3>
<P>
<NumberFormat signDisplay="negative" value={1234} />
<NumberFormat signDisplay="negative" value={-1234} />
<NumberFormat signDisplay="negative" value={0} />
</P>
<H3>signDisplay="exceptZero"</H3>
<P>
<NumberFormat signDisplay="exceptZero" value={1234} />
<NumberFormat signDisplay="exceptZero" value={-1234} />
<NumberFormat signDisplay="exceptZero" value={0} />
</P>
</ComponentBox>
</Style>
)
```
### Using the Provider with NumberFormat
In this example every NumberFormat will receive the Provider defined properties, including `clean_copy_value`.
```tsx
render(
<Style>
<ComponentBox>
<Provider
value={{
NumberFormat: {
currency: true,
rounding: 'omit',
clean_copy_value: true,
},
}}
>
<P>
<NumberFormat>12345</NumberFormat>
<NumberFormat value={-12345.123} decimals={0} />
<NumberFormat value={-12345678.955} currency_position="before" />
</P>
</Provider>
</ComponentBox>
</Style>
)
```
### Monospace
By using the `monospace` property you can set the font to [DNB Mono Regular](/quickguide-designer/fonts/#dnbmono-regular)
```tsx
render(
<Style>
<ComponentBox data-visual-test="number-format-monospace">
<NumberFormat
value="123456"
locale="en-GB"
currency="NOK"
monospace
/>
</ComponentBox>
</Style>
)
```
## Properties
<PropertiesTable props={NumberFormatProperties} />
## Translations
```json
{
"locales": ["da-DK", "en-GB", "nb-NO", "sv-SE"],
"entries": {
"NumberFormat.clipboard_copy": {
"nb-NO": "Kopiert",
"en-GB": "Copied",
"sv-SE": "Kopierad",
"da-DK": "Kopieret"
},
"NumberFormat.not_available": {
"nb-NO": "Ikke tilgjengelig",
"en-GB": "Not available",
"sv-SE": "Inte tillgänglig",
"da-DK": "Ikke tilgængelig"
}
}
}
```