Number formatting in JavaScript: toLocaleString and Intl.NumberFormat

JavaScript has built-in APIs for locale-aware number formatting that cover the vast majority of i18n use cases without any third-party dependencies. This post focuses on toLocaleString() and Intl.NumberFormat, the two main tools, with practical examples for currencies, percentages, and units.
For a broader guide covering dates, times, time zones, and currencies together, see Handling dates, times, numbers, and currencies in i18n. Correct number formatting is one of the foundational i18n concerns, alongside date handling, pluralization, and locale detection, all covered in our complete technical guide to internationalization and software localization.
toLocaleString() vs Intl.NumberFormat
Both APIs format numbers locale-correctly. The difference is performance. Intl.NumberFormat is more efficient when you need to format many numbers with the same options because you create the formatter once and reuse it:
// toLocaleString: creates a formatter on every call
[1000, 2000, 3000].map(n => n.toLocaleString('de-DE'));
// Intl.NumberFormat: create once, format many
const fmt = new Intl.NumberFormat('de-DE');
[1000, 2000, 3000].map(n => fmt.format(n));
For one-off formatting, toLocaleString() is perfectly fine and more concise. Both accept the same locale and options arguments.
Syntax
// toLocaleString
number.toLocaleString(locale, options);
// Intl.NumberFormat
new Intl.NumberFormat(locale, options).format(number);
Parameters
locale(optional): if not provided, the method uses the host environment's current locale (e.g., default browser language). Passundefinedto use the system locale explicitly.options(optional): object with formatting options
const n = 123456.789;
n.toLocaleString('pl-PL');
// → "123 456,789"
n.toLocaleString('ar-EG');
// → "١٢٣٬٤٥٦٫٧٨٩"
Limit significant digits
const price = 123456.789;
price.toLocaleString('en-IN', {
maximumSignificantDigits: 2
});
// → "1,20,000"
Use system locale with controlled decimal places
Pass undefined as the first parameter to use the browser's default locale.
const price = 30000.65;
price.toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
// English output: 30,000.65
// German output: 30.000,65
// French output: 30 000,65
Formatting style
The style property accepts four values:
decimal(default)currencypercentunit
Style: Currencies
Use style property in options object with value currency to format number into a string.
const price = 123456.789;
price.toLocaleString('de-DE', { style: 'currency', currency: 'EUR' });
// → "123.456,79 €"
price.toLocaleString('ja-JP', { style: 'currency', currency: 'JPY' });
// → "¥123,457"
Note that JPY has no subunits, so the decimal is correctly omitted. Use minimumFractionDigits and maximumFractionDigits when you need consistent decimal places:
price.toLocaleString('en-US', {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
maximumFractionDigits: 2,
});
// → "$123,456.79"
The currencyDisplay property changes how the currency identifier appears:
const price = 123456.789;
price.toLocaleString('de-DE', {
style: 'currency',
currencyDisplay: 'code',
currency: 'EUR'
});
// → "123.456,79 EUR"
price.toLocaleString('ja-JP', {
style: 'currency',
currencyDisplay: 'name',
currency: 'JPY'
});
// → "123,457円"
Possible currencyDisplay values: symbol (default), code, name, narrowSymbol.
Style: Percentages
Percentage sign placement and numeral style both vary by locale.
const value = 0.767;
value.toLocaleString('pl-PL', { style: 'percent' });
// → "77%"
value.toLocaleString('ar-SA', { style: 'percent' });
// → "٧٧٪"
value.toLocaleString('tr-TR', { style: 'percent' });
// → "%77" (sign before the number in Turkish)
value.toLocaleString('fr-FR', { style: 'percent' });
// → "77 %" (space before percent sign in French)
Style: Units
One of the most underused JavaScript locale features. It formats numbers into any of the standard measurement units with proper locale-appropriate notation.
Liter
const value = 3;
value.toLocaleString('pl-PL', { style: 'unit', unit: 'liter' });
// → "3 l"
value.toLocaleString('pl-PL', { style: 'unit', unit: 'liter', unitDisplay: 'long' });
// → "3 litry"
value.toLocaleString('pl-PL', { style: 'unit', unit: 'liter', unitDisplay: 'narrow' });
// → "3l"
unitDisplay accepts: short (default), long, narrow.
Kilometer per hour
Compound units use X-per-Y syntax. JavaScript picks the appropriate locale pattern:
const speed = 50.2137;
speed.toLocaleString('pt-PT', { style: 'unit', unit: 'kilometer-per-hour' });
// → "50,214 km/h"
speed.toLocaleString('en-US', { style: 'unit', unit: 'kilometer-per-hour' });
// → "50.214 km/h"
Supported units
Below, you can check all possible values for unit property.
unit value |
|---|
| acre |
| bit |
| byte |
| celsius |
| centimeter |
| day |
| degree |
| fahrenheit |
| fluid-ounce |
| foot |
| gallon |
| gigabit |
| gigabyte |
| gram |
| hectare |
| hour |
| inch |
| kilobit |
| kilobyte |
| kilogram |
| kilometer |
| liter |
unit value |
|---|
| megabit |
| megabyte |
| meter |
| mile |
| mile-scandinavian |
| milliliter |
| millimeter |
| millisecond |
| minute |
| month |
| ounce |
| percent |
| petabyte |
| pound |
| second |
| stone |
| terabit |
| terabyte |
| week |
| yard |
| year |
Terabyte per gram
The unit property doesn't have to make sense, it accepts any combination. 😊
const value = 50.2137;
value.toLocaleString('pl-PL', {
style: 'unit',
unit: 'terabyte-per-gram',
unitDisplay: "long"
});
// output: 50,214 terabajta na gram
Compact notation
For dashboards and analytics UIs where space is limited:
const value = 1500000;
new Intl.NumberFormat('en-US', { notation: 'compact' }).format(value);
// → "1.5M"
new Intl.NumberFormat('de-DE', { notation: 'compact' }).format(value);
// → "1,5 Mio."
new Intl.NumberFormat('ja-JP', { notation: 'compact' }).format(value);
// → "150万" (万 = 10,000 in Japanese grouping)
Accounting notation
Negative amounts shown in parentheses rather than with a minus sign, common in financial contexts:
const loss = -1234.56;
new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
currencySign: 'accounting',
}).format(loss);
// → "($1,234.56)"




