JavaScript Thousands Separators: Add Commas and Parse Input
Use toLocaleString("en-US") or Intl.NumberFormat("en-US") to display a number with thousands commas. To calculate with a comma-separated input, validate its decimal format and comma positions before removing the commas and converting it. Keep the displayed string separate from the numeric value used for calculations.
Specify a locale for comma grouping
(123456789).toLocaleString("en-US"); // "123,456,789"
(123456789).toLocaleString("de-DE"); // "123.456.789"
const formatter = new Intl.NumberFormat("en-US");
formatter.format(123456789); // "123,456,789"
A locale identifies language and regional formatting rules. If you omit it, the environment chooses a default, so commas are not guaranteed. Reuse an Intl.NumberFormat instance when formatting many values with the same options. Both approaches return strings. See Number.toLocaleString() and Intl.NumberFormat.
Control decimal places and currency display
const decimal = new Intl.NumberFormat("en-US", {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
decimal.format(1234.5); // "1,234.50"
const won = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "KRW"
});
won.format(123456789); // Displays the KRW symbol and digit grouping
Set both minimumFractionDigits and maximumFractionDigits when you need a fixed display width and a clear rounding limit. With the default plain-decimal options, the maximum is three fractional digits; specify options to retain more. Currency formatting uses currency-specific defaults. The currency example intentionally keeps KRW while applying the en-US locale. Formatting rounds the displayed representation without changing the original variable. NumberFormat constructor options describes how these defaults interact.
Removing commas does not convert a string to a number
const text = "123,456,789"; const plain = text.replace(/,/g, ""); plain; // "123456789" typeof plain; // "string" Number(plain); // 123456789
The regular expression /,/g finds every comma: g applies the replacement globally. replace() returns a new string, so the result still needs Number() if you want numeric arithmetic. A regular expression describes a text pattern for matching or replacement.
Blind replacement is not input validation. The incorrectly grouped "12,34" becomes 1234 when commas are simply removed, hiding the error. Deleting all nondigit characters is worse: it can turn -12.5 into 125. Validate the format you actually accept before transforming the value.
Complete example: validate the input format first
Save the following as number-grouping.html in UTF-8 and open it in a browser. It accepts surrounding whitespace, an optional sign, an integer part and an optional decimal fraction. If commas are present, the first group must contain one to three digits and every subsequent group exactly three.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Thousands separators and input validation</title>
<style>
body { margin: 24px; font: 16px/1.7 sans-serif; }
input, button, select { font: inherit; max-width: 100%; }
label { display: block; margin-top: 12px; }
#output { white-space: pre-wrap; overflow-wrap: anywhere; }
</style>
</head>
<body>
<label for="value">Decimal number, with or without commas</label>
<input id="value" type="text" value="1,234,567.89">
<button id="format" type="button">Validate and format</button>
<pre id="output" aria-live="polite"></pre>
<script>
function parseGroupedDecimal(input) {
const text = input.trim();
const pattern = /^[+-]?(?:\d+|\d{1,3}(?:,\d{3})+)(?:\.\d+)?$/;
if (!pattern.test(text)) return null;
const value = Number(text.replace(/,/g, ""));
return Number.isFinite(value) ? value : null;
}
const formatter = new Intl.NumberFormat("en-US", {
useGrouping: true,
maximumFractionDigits: 20
});
function show() {
const input = document.getElementById("value").value;
const value = parseGroupedDecimal(input);
const output = document.getElementById("output");
if (value === null) {
output.textContent = "Check the comma positions and number format.";
return;
}
output.textContent = `Number: ${value}\nDisplay: ${formatter.format(value)}\nType: ${typeof value}`;
}
document.getElementById("format").addEventListener("click", show);
show();
</script>
</body>
</html>
The initial input becomes the number 1234567.89 and displays as 1,234,567.89. The values -12,345.6 and 0 are accepted; 12,34, 1,234,, ₩1,000 and a blank string are rejected. The parser returns null for failure so that valid zero remains distinguishable. maximumFractionDigits: 20 is a display setting and does not increase Number precision.
Group a validated decimal string without changing its fraction
const text = "-1234567.8900";
const [integer, fraction] = text.split(".");
const grouped = integer.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
const result = fraction === undefined
? grouped
: `${grouped}.${fraction}`;
console.log(result); // -1,234,567.8900
This approach groups only the integer portion of an already validated decimal string. Splitting off the fraction prevents a grouping pattern from inserting commas into fractional digits. String processing is useful when trailing zeros such as .8900 must survive, but this is not a general formatter for exponent notation or every regional number format.
Regional formats and large integer limits
- The German-format input "1.234,56" is outside this parser’s accepted grammar. Intl.NumberFormat formats values; it is not a universal inverse parser. Define the input locale and rules explicitly.
- Number can lose integer digits beyond its safe range. Retain the original string when exact digits matter. BigInt can represent permitted integer inputs, but does not store fractional values.
- Use Number() conversion and validation for finite-value checks, and floor, ceil, round and trunc when choosing how to round calculated values.

