Locale-aware number and currency formatting

Intl.NumberFormat

Baseline widely available
  • Chrome24
  • Edge12
  • Firefox29
  • Safari10

Features it needs

  • IntlWidely available

These libraries hardcode their own thousands separators, currency symbols, and rounding rules, and most ship a single locale by default. Intl.NumberFormat is the same formatting logic the operating system already has: pass a locale and a style, currency, percent, or unit, and it places the symbol, separator, and decimal grouping the way that locale actually expects, with no formatting rules to maintain.

When this applies

Formatting a number as currency, a percentage, or a locale-correct thousands-grouped number for display.

The native approach

new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD",
}).format(1234.5); // "$1,234.50"

MDN reference

When the dependency is still right

An answer that always says "the platform covers it" is worse than no answer. These are the cases where this one does not hold.

  • You format units or compact notation. style: "unit" and notation: "compact" arrived long after the rest of Intl.NumberFormat, in Chrome 77, Firefox 78 and Safari 14.1, so an older target does not get them.
  • You need safe decimal arithmetic, such as adding money values without floating-point rounding errors. currency.js and accounting.js do that math for you; Intl.NumberFormat only formats a number you already computed correctly.
  • You need to parse a formatted string back into a number. Intl.NumberFormat is format-only; these libraries often provide the reverse direction too.
  • You format byte sizes and need the unit chosen for you. Intl.NumberFormat formats a magnitude you already picked, so "kilobyte" on 1200 prints 1,200 kB rather than 1.2 MB, and dividing down to the right unit first is the part pretty-bytes and filesize actually do.
  • You need binary units. The sanctioned unit list has no kibibyte, mebibyte or gibibyte, and asking for one throws a RangeError rather than falling back, so anything reporting KiB or MiB keeps its library.
  • You need a custom format pattern, such as a specific abbreviation style, that the options Intl.NumberFormat exposes cannot express.

Signs it was hand-rolled

No package is involved in any of these, so nothing would match in a package.json. If the code looks like one of them, this rule applies anyway, and the conditions above still decide.

  • a regex inserting thousands separators into a number's string form
  • toFixed followed by string surgery to add a currency symbol and group the digits

Packages this covers