next-intl provides two approaches for number formatting: using the useFormatter hook for numbers outside translation messages, and embedding ICU number syntax directly within message strings for integrated formatting [citation:1].
next-intl handles number formatting in a way that respects the user's locale, automatically adapting decimal separators, digit grouping, and currency symbol placement [citation:1][citation:3]. The library offers two distinct methods to format numbers, depending on whether the number is part of a larger translated string or a standalone UI element.
For formatting numbers that are not part of a translated message (e.g., a price display, a chart label, or a statistic), you use the useFormatter hook. This gives you access to a format object containing a number method [citation:1][citation:4]. This method is a wrapper around the browser's native Intl.NumberFormat and accepts the same standard options [citation:1][citation:6].
If you have global formats defined in your i18n/request.ts configuration, you can reference them by name as the second argument [citation:1][citation:3]. This allows you to maintain a consistent number style across your application. For example, if you have a global format named 'precise' with maximumFractionDigits: 5, you can use format.number(3.14159, 'precise') [citation:1][citation:2].
When numbers are part of a translated sentence (e.g., "You have {count} new messages"), you should format them using the ICU Message Syntax directly within your message string. This keeps all the text for a sentence in one place, which is much easier for translators to work with [citation:1][citation:3].
Basic Formatting: {value, number} formats the number with default locale settings [citation:1][citation:6].
Currency: {value, number, currency} requires you to define the currency type via the currency option in the third argument of the t() function [citation:3].
Percent: {value, number, percent} multiplies the value by 100 and appends the percent sign [citation:1].
Number Skeletons: {value, number, ::.##} uses a 'skeleton' (denoted by ::) for precise control over the format, such as setting the number of fraction digits [citation:1][citation:3].
For complex or reusable number formats inside messages, you can pass custom number options as the third argument to the t function. This is especially useful for defining the currency when using {value, number, currency} [citation:1][citation:6].
For formatting numbers in Server Components, you can use the getFormatter function. This is the asynchronous equivalent of the client-side useFormatter hook [citation:4]. The pattern remains the same: you call format.number() with the value and options. This ensures consistent formatting on the server before the HTML is sent to the client.