Question

Format Numbers With Thousands Separators in JavaScript

javascriptformattingnumbers

Question

I want to display a number in JavaScript with commas as thousands separators. For example, I want the number 1234567 to appear as "1,234,567".

Here is the approach I am currently using:

function numberWithCommas(x) {
    x = x.toString();
    var pattern = /(-?\d+)(\d{3})/;
    while (pattern.test(x)) {
        x = x.replace(pattern, "$1,$2");
    }
    return x;
}

console.log(numberWithCommas(1000));

Is there a simpler or more elegant way to do this?

It would be nice if the solution also worked with floating-point numbers, although that is not strictly required. It does not need to be locale-specific for choosing commas versus periods.

Short Answer

By the end of this page, you will understand how to format numbers with thousands separators in JavaScript, when to use built-in methods like toLocaleString() and Intl.NumberFormat, and when a custom formatting function is useful.

Concept

When you display large numbers to users, readability matters. A value like 1234567 is harder to scan than 1,234,567. Adding separators every three digits makes the number easier to understand quickly.

In JavaScript, number formatting is usually a display concern rather than a math concern. That means you typically keep the value as a number for calculations, and only format it as a string when showing it in the UI, logs, reports, or exported output.

The most important idea is this:

  • Numbers are for computation
  • Formatted strings are for display

JavaScript gives you built-in tools for this:

  • number.toLocaleString() for simple formatting
  • Intl.NumberFormat for reusable formatting logic

You can also write a custom solution, often with a regular expression, if you want fixed behavior that does not depend on locale settings.

This concept matters in real programming because formatted numbers appear everywhere:

  • dashboards
  • invoices
  • admin panels
  • analytics reports
  • e-commerce prices
  • financial summaries

If you format numbers incorrectly, users may misunderstand values, especially for money, counts, or measurements.

Mental Model

Think of a large number as a long word that is hard to read without spaces.

For example:

  • 1234567 is like thisisaverylongword
  • 1,234,567 is like this is a very long word

The commas act like visual separators. They do not change the value, but they make it much easier for people to read.

So the job of number formatting is like adding punctuation to a sentence: the meaning stays the same, but the result becomes clearer.

Take Quiz

Syntax and Examples

The simplest built-in way in JavaScript is toLocaleString().

const num = 1234567;
console.log(num.toLocaleString());
// Example output: "1,234,567"

This also works with decimals:

const num = 1234567.89;
console.log(num.toLocaleString());
// Example output in many environments: "1,234,567.89"

If you want more control or need to reuse the formatter, use Intl.NumberFormat:

const formatter = new Intl.NumberFormat('en-US');

console.log(formatter.format(1234567));
// "1,234,567"

console.log(formatter.format(1234567.89));
// "1,234,567.89"

A custom regex-based approach is also possible:

function numberWithCommas(value) {
    return String(value).replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}

console.log(numberWithCommas(1234567));
// "1,234,567"

That regex inserts commas at positions that are followed by groups of three digits.

If you want a regex version that handles decimals more safely, split the integer and fractional parts:

function formatNumber(value) {
    const [integerPart, decimalPart] = String(value).split('.');
    const formattedInteger = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
    return decimalPart ? `${formattedInteger}.${decimalPart}` : formattedInteger;
}

console.log(formatNumber(1234567));
// "1,234,567"

console.log(formatNumber(1234567.89));
// "1,234,567.89"

For most real code, prefer toLocaleString() or Intl.NumberFormat unless you specifically need custom non-locale behavior.

Step by Step Execution

Consider this example:

const num = 1234567.89;
const result = num.toLocaleString('en-US');
console.log(result);

Step by step:

  1. num stores the number 1234567.89.
  2. toLocaleString('en-US') is called on that number.
  3. JavaScript converts the number into a formatted string using the en-US style.
  4. In en-US, thousands are separated by commas and decimals use a period.
  5. The returned value is the string "1,234,567.89".
  6. console.log(result) prints that string.

Now compare that to a custom regex version:

function formatNumber(value) {
    const [integerPart, decimalPart] = String(value).split('.');
    const formattedInteger = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
    return decimalPart ? `${formattedInteger}.${decimalPart}` : formattedInteger;
}

console.log(formatNumber(1234567.89));

Step by step:

  1. String(value) turns the number into the string "1234567.89".
  2. .split('.') breaks it into:
    • integerPart = "1234567"
    • decimalPart = "89"
  3. The regex runs only on the integer part.
  4. It inserts commas before every group of three digits from the right.
  5. The integer becomes "1,234,567".
  6. The decimal part is added back.
  7. The final result is "1,234,567.89".

This shows the difference between using a built-in formatter and manually formatting the string yourself.

Real World Use Cases

Number formatting is common in many kinds of software.

  • E-commerce: showing product prices, order totals, and discounts
  • Analytics dashboards: displaying page views, revenue, or user counts
  • Finance apps: formatting balances, transactions, and account summaries
  • Admin panels: presenting IDs, counts, and metrics clearly
  • Reports and exports: making CSV previews or PDF summaries more readable
  • APIs and UI layers: formatting values before rendering them to users

Examples:

const totalUsers = 2500000;
console.log(totalUsers.toLocaleString('en-US'));
// "2,500,000"
const revenue = 9876543.21;
console.log(new Intl.NumberFormat('en-US').format(revenue));
// "9,876,543.21"

A key point: the stored value should usually remain numeric, while the formatted version should be created only when needed for display.

Take Quiz

Real Codebase Usage

In real projects, developers usually avoid hand-written regex formatting unless they have a specific reason. Common patterns include:

Use built-in formatters in UI code

const users = 1234567;
const displayUsers = users.toLocaleString('en-US');

This is simple and readable.

Reuse a formatter object

If you format many numbers, create one formatter and reuse it:

const numberFormatter = new Intl.NumberFormat('en-US');

function renderStats(count) {
    return numberFormatter.format(count);
}

This is common in dashboards and reporting code.

Keep formatting at the presentation layer

const subtotal = 1234567.89;
const tax = 1000;
const total = subtotal + tax;

console.log(total); // raw number for logic
console.log(total.toLocaleString('en-US')); // formatted for display

Do not store the formatted string if you still need to do math with the value.

Use options when needed

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

console.log(formatter.format(1234567));
// "1,234,567.00"

This pattern is common for totals and financial displays.

Guard against invalid input

function safeFormatNumber(value) {
    if (typeof value !== 'number' || Number.isNaN(value)) {
        return 'Invalid number';
    }
    return value.toLocaleString('en-US');
}

Validation like this is common when values come from forms, APIs, or user input.

Common Mistakes

Here are common beginner mistakes when formatting numbers.

1. Formatting too early

Broken example:

let total = (1000).toLocaleString('en-US');
total += 500;
console.log(total);
// "1,000500"

Why it happens:

  • toLocaleString() returns a string, not a number.
  • Adding 500 to a string causes string concatenation.

Better:

let total = 1000;
total += 500;
console.log(total.toLocaleString('en-US'));
// "1,500"

2. Assuming formatting always uses commas

console.log((1234567).toLocaleString());

This depends on environment locale. In some locales, the output may not use commas.

If you specifically want commas, pass a locale such as 'en-US':

console.log((1234567).toLocaleString('en-US'));

3. Applying a regex directly to decimals without thinking

Broken example:

function format(value) {
    return String(value).replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}

console.log(format(12345.67));

This may work in many simple cases, but custom regex solutions can become fragile when requirements grow.

Safer custom approach:

function format(value) {
    const [integerPart, decimalPart] = String(value).split('.');
    const formattedInteger = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
    return decimalPart ? `${formattedInteger}.${decimalPart}` : formattedInteger;
}

4. Forgetting negative numbers

If you write a custom formatter, make sure -1234567 becomes -1,234,567.

5. Using formatted strings in calculations

Broken example:

const amount = '1,234,567';
console.log(amount * 2);
// NaN

Why it happens:

  • JavaScript cannot directly multiply a comma-formatted string.

Keep a raw numeric value for calculations.

6. Confusing display formatting with rounding

toLocaleString() formats output, but options may also affect decimal display. Be clear whether you want:

  • separators only
  • fixed decimal places
  • rounded values

Comparisons

ApproachBest forHandles decimalsLocale-awareReusableNotes
toLocaleString()Quick formattingYesYesMediumEasiest built-in option
Intl.NumberFormatRepeated formatting with optionsYesYesHighBest for apps with many formatted values
Regex replacementCustom fixed formattingSometimesNoMediumUseful when you want strict custom behavior

toLocaleString() vs Intl.NumberFormat

FeaturetoLocaleString()Intl.NumberFormat
Easy to use onceYesYes
Best for many repeated callsNoYes
Supports formatting optionsYesYes
Cleaner for reusable codeNoYes

Built-in vs custom regex

Built-in methodsCustom regex
More reliable for general useMore control over exact output
Handle locale rulesIgnores locale unless you implement it
Easier to readCan be harder to understand
Better for production UIBetter for niche fixed-format cases
Take Quiz

Cheat Sheet

// Quick built-in formatting
(1234567).toLocaleString('en-US');
// "1,234,567"

// With decimals
(1234567.89).toLocaleString('en-US');
// "1,234,567.89"

// Reusable formatter
const formatter = new Intl.NumberFormat('en-US');
formatter.format(1234567);
// "1,234,567"

// Custom regex formatter
function formatNumber(value) {
  const [integerPart, decimalPart] = String(value).split('.');
  const formattedInteger = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
  return decimalPart ? `${formattedInteger}.${decimalPart}` : formattedInteger;
}

Rules to remember

  • Use numbers for calculations.
  • Use formatted strings for display.
  • toLocaleString() returns a string.
  • Pass 'en-US' if you specifically want commas and periods.
  • Use Intl.NumberFormat when formatting many values.
  • Use custom regex only when you need fixed non-locale behavior.

Edge cases

  • Negative numbers: -1234567 → -1,234,567
  • Decimals: 1234.56 → 1,234.56
  • Invalid numbers: check with Number.isNaN(value)
  • Locale differences: default output may vary by environment

FAQ

What is the simplest way to add commas to a number in JavaScript?

Use toLocaleString():

(1234567).toLocaleString('en-US');

Does toLocaleString() work with decimal numbers?

Yes. It can format both integers and floating-point numbers.

Should I use regex or toLocaleString()?

Use toLocaleString() or Intl.NumberFormat in most cases. Use regex only if you need a custom fixed format.

Why does toLocaleString() sometimes not use commas?

Because its output depends on locale. If you want commas specifically, pass 'en-US'.

Can I format negative numbers too?

Yes. Built-in methods handle negative numbers automatically.

Is the formatted result still a number?

No. It is a string. That means it should usually be used only for display.

What is the difference between toLocaleString() and Intl.NumberFormat?

toLocaleString() is great for quick one-off formatting. Intl.NumberFormat is better when you want to reuse a formatter or configure options in one place.

Take Quiz

Mini Project

Description

Build a small number-formatting utility that displays user-friendly values for a reporting dashboard. This project demonstrates how to format raw numeric data before showing it to users, while keeping the original values usable for calculations.

Goal

Create a reusable JavaScript function that formats integers and decimals with thousands separators and safely handles invalid input.

Requirements

  • Create a function that accepts a numeric value.
  • Return the value with commas as thousands separators.
  • Support both integers and decimal numbers.
  • Handle negative numbers correctly.
  • Return a clear message for invalid input.
Take Quiz

Keep learning

Related questions

View more