Intro

While working on an interactive data visualization I needed to represent a number as a percent for display purposes. Fortunately, thanks to the widely supported Intl.NumberFormat API, this common use case was easy to accomplish with a few lines of code!

Basic formatting

Representing a number as a percent with Intl.NumberFormat begins with adding style: 'percent' as an option when calling the formatter factory function. This option is all that’s required to create a basic formatter with sensible defaults:

const formatter = Intl.NumberFormat('en-US', {
  style: 'percent',
})

console.log(formatter.format(0.12))   // 12%

A single formatter instance of Intl.NumberFormat can also be reused to format multiple numbers:

const formatter = Intl.NumberFormat('en-US', {
  style: 'percent',
})

console.log(formatter.format(0.12))  // 12%
console.log(formatter.format(0.04))  // 4%
console.log(formatter.format(1))     // 100%

Configuring decimal precision

You may be wondering what the output of the format function is if our numbers aren’t so precise? All of the example numbers we’ve seen so far are formatted as a nice round integer: 12%, 4%, 100%. What happens, though, when the number we’d want to format has more than 2 decimal places? By default NumberFormat rounds to the nearest whole number; possibly resulting in unexpected values. Example:

const formatter = Intl.NumberFormat('en-US', {
  style: 'percent',
})

console.log(formatter.format(0.125))   // 13%
console.log(formatter.format(0.0411))  // 4%
console.log(formatter.format(1.001))   // 100%

As you saw above, 0.125 was rounded up to 13%, when we may have desired an output of 12.5% instead. This degree of rounding would be inaccurate and unacceptable for many data driven purposes. Fortunately, two configuration values give us precise control over this rounding behavior: minimumFractionDigits and maximumFractionDigits.

  • minimumFractionDigits is used to configure how many digits should always be displayed. If the number being formatted does not have digits at those places, zeroes are rendered instead:
const formatter = Intl.NumberFormat('en-US', {
  style: 'percent',
  minimumFractionDigits: 2,
})

console.log(formatter.format(0.125))    // 12.50%
console.log(formatter.format(0.04001))  // 4.00%
console.log(formatter.format(1))        // 100.00%
  • maximumFractionDigits configures the maximum number of decimal places that should be displayed, however zeroes are not filled in if those places are non-zero.
const formatter = Intl.NumberFormat('en-US', {
  style: 'percent',
  maximumFractionDigits: 2,
})

console.log(formatter.format(0.125))    // 12.5%
console.log(formatter.format(0.12001))  // 12%
console.log(formatter.format(0.04111))  // 4.11%
console.log(formatter.format(1))        // 100%

Combining both options together gives us a more predictable and user friendly output. If you desired to always display two decimal places regardless of what the values are, you can set both options to 2:

const formatter = Intl.NumberFormat('en-US', {
  style: 'percent',
  minimumFractionDigits: 2,
  maximumFractionDigits: 2,
})

console.log(formatter.format(0.125))    // 12.50%
console.log(formatter.format(0.12001))  // 12.00%
console.log(formatter.format(0.04111))  // 4.11%
console.log(formatter.format(1))        // 100.00%

Note that when we used these options the aforementioned rounding still occured- however we had control over how many digits were significant and would be retained.

Using locales

The Intl.NumberFormat API also supports different locales via the first argument. Setting this parameter to the desired locale allows numbers to be formatted according to a region/language specific style. Using our percent format example from above, we were able to easily percentage format a number for a German locale:

const formatter = Intl.NumberFormat('de-DE', {
  style: 'percent',
  minimumFractionDigits: 2,
  maximumFractionDigits: 2,
})

console.log(formatter.format(0.123))   // 12,30 %

Conclusion

Hopefully this post showed how useful Intl.NumberFormat API can be for formatting percentages. There’s even more options and features that make this API awesome!