[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"verticals":3,"article-intl-numberformat-currency-formatting":44,"search-suggestions":289,"path-membership-intl-numberformat-currency-formatting":329,"related-intl-numberformat-currency-formatting":330,"code:js:true:cj4aqm":464,"code:js:true:kcdjj4":465,"code:js:true:11idgdm":466,"code:js:true:1k34w3e":467,"code:js:true:1jz93zz":468,"code:js:true:jt5509":469,"code:js:true:eklaps":470,"code:js:true:1esq1lz":471,"code:js:true:phrr5m":472,"code:js:true:3ylg1t":473,"comments-01a13e2e-f495-74ab-b10e-7e8cebdfc365":474},[4,20,32],{"id":5,"slug":6,"name":7,"tagline":8,"description":9,"accentFrom":10,"accentTo":11,"icon":12,"defaultLocale":13,"locales":14,"features":16,"position":19},"019fe637-3d33-714b-b57f-23e163ffca0c","dev","Web Development","Read it. Run it. Prove it.","A post a day on modern web development — most with an editable playground and a quiz that explains every answer. Free, no account needed.","violet-500","cyan-400","◇","en",[13,15],"fa",{"courses":17,"paths":17,"articles":17,"exams":18,"flashcards":18,"packages":17,"community":17,"certificates":17,"teams":17,"commerce":17},true,false,0,{"id":21,"slug":22,"name":23,"tagline":24,"description":25,"accentFrom":26,"accentTo":10,"icon":27,"defaultLocale":13,"locales":28,"features":30,"position":31},"019fe637-3dc2-754c-8657-0f175bfee7c6","lang","Languages","Learn a language the way you learn a codebase.","Grammar explained the way good documentation explains an API — one idea at a time, each with a quiz.","amber-400","⌘",[13,15,29],"es",{"courses":18,"paths":18,"articles":17,"exams":18,"flashcards":17,"packages":18,"community":17,"certificates":17,"teams":18,"commerce":18},2,{"id":33,"slug":34,"name":35,"tagline":36,"description":37,"accentFrom":38,"accentTo":39,"icon":40,"defaultLocale":13,"locales":41,"features":42,"position":43},"7b3c16f2-931d-410e-802e-e1fa4edab7de","soft","Soft Skills","The half of the job nobody wrote documentation for.","Weekly, on the parts of working life that decide more than your code does — first weeks, meetings, interviews, promotions, and the people around you. Written from what actually happens, and recorded as a podcast you can listen to on the walk.","emerald-400","teal-300","◉",[13],{"courses":18,"paths":18,"articles":17,"exams":18,"flashcards":18,"packages":18,"community":17,"certificates":18,"teams":18,"commerce":18},3,{"id":45,"slug":46,"title":47,"subtitle":48,"excerpt":49,"coverUrl":50,"locale":13,"readingMinutes":51,"publishedAt":52,"viewCount":53,"likeCount":19,"commentCount":19,"author":54,"vertical":59,"topic":60,"tags":63,"_count":75,"playground":77,"body":79,"bodyMd":272,"seo":273,"translationGroupId":276,"series":48,"podcastUrl":48,"verticalId":5,"thread":277,"assessments":279,"translations":285,"quiz":287},"01a13e2e-f495-74ab-b10e-7e8cebdfc365","intl-numberformat-currency-formatting","Stop Formatting Currency With Regex. `Intl.NumberFormat` Does It.",null,"A hand-rolled formatMoney() with toFixed() and a comma regex looks fine in a US demo and quietly breaks the moment a real user, a negative balance, or a non-dollar locale shows up. Intl.NumberFormat has handled all of it, built into every modern browser, since before most teams' codebases existed.","\u002Fmedia\u002Fcovers\u002Fintl-numberformat-currency-formatting.png",5,"2026-10-20T06:10:28.425Z",47,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},"019fe637-3c25-7088-9034-39c9f15dc3c8","Parsa Jiravand","parsa","Frontend engineer · building bestpractic",{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},"javascript","JavaScript",[64,66,69,72],{"slug":61,"name":65,"color":48},"Javascript",{"slug":67,"name":68,"color":48},"webdev","Webdev",{"slug":70,"name":71,"color":48},"typescript","Typescript",{"slug":73,"name":74,"color":48},"programming","Programming",{"assessments":76},1,{"slug":46,"title":78},"Intl.NumberFormat vs. hand-rolled formatMoney()",{"blocks":80,"version":76},[81,85,91,94,97,101,104,108,111,115,118,134,137,141,144,148,151,155,158,161,164,168,171,175,178,182,185,188,191,195,199,202,205,208,211,214,217,220,223,226,229,232,235,238,241,244,247,254,257,260,263,266],{"id":82,"html":83,"type":84},"b1","\u003Cp>Open any finance dashboard&#39;s test suite and there&#39;s a decent chance you&#39;ll find a function like this, usually written once, two years ago, by someone who is no longer on the team:\u003C\u002Fp>","paragraph",{"id":86,"code":87,"type":88,"language":89,"highlight":90},"b2","function formatMoney(amount) {\n  return '$' + amount.toFixed(2).replace(\u002F\\B(?=(\\d{3})+(?!\\d))\u002Fg, ',');\n}","code","js",[],{"id":92,"html":93,"type":84},"b3","\u003Cp>It passes its tests. \u003Ccode>formatMoney(1234.5)\u003C\u002Fcode> returns \u003Ccode>&quot;$1,234.50&quot;\u003C\u002Fcode>, which is exactly what the ticket asked for. Nobody has touched it since, because it works — until a refund comes through as a negative number, or a stakeholder asks for a per-unit cost that needs four decimal places, or the company opens an EU region and legal asks why every price shows up with a comma where European customers expect a period.\u003C\u002Fp>",{"id":95,"html":96,"type":84},"b4","\u003Cp>None of those are edge cases. They&#39;re Tuesday.\u003C\u002Fp>",{"id":98,"html":99,"text":99,"type":100,"level":31},"b5","The fix everyone reaches for: patch the regex","heading",{"id":102,"html":103,"type":84},"b6","\u003Cp>The usual response to &quot;it broke for amount X&quot; is to patch the one case that broke:\u003C\u002Fp>",{"id":105,"code":106,"type":88,"language":89,"highlight":107},"b7","function formatMoney(amount) {\n  const sign = amount \u003C 0 ? '-' : '';\n  const fixed = Math.abs(amount).toFixed(2);\n  return sign + '$' + fixed.replace(\u002F\\B(?=(\\d{3})+(?!\\d))\u002Fg, ',');\n}",[],{"id":109,"html":110,"type":84},"b8","\u003Cp>That fixes negatives. It does not fix the next report, four decimal places: \u003Ccode>formatMoney(1234.5678)\u003C\u002Fcode> still runs the same comma-grouping regex across the \u003Cem>entire\u003C\u002Fem> string, digits after the decimal point included — so instead of \u003Ccode>&quot;$1,234.5678&quot;\u003C\u002Fcode> it can silently insert a comma inside the fractional part too, because the regex has no idea where the decimal point is relative to its own grouping. And none of this touches the actual question underneath the ticket: a German user doesn&#39;t want \u003Ccode>&quot;1,234.50&quot;\u003C\u002Fcode> at all. They want \u003Ccode>&quot;1.234,50&quot;\u003C\u002Fcode> — comma and period swapped — followed by a \u003Ccode>€\u003C\u002Fcode> with a non-breaking space in front of it, not a \u003Ccode>$\u003C\u002Fcode> in front with no space. A regex built around US formatting conventions can&#39;t produce that output by patching cases one at a time, because it never encoded &quot;how do numbers look in Germany&quot; in the first place. There&#39;s no case to patch for that; the whole function is missing the concept.\u003C\u002Fp>",{"id":112,"html":113,"text":114,"type":100,"level":31},"b9","What&#39;s actually going on: formatting money needs locale data, not string surgery","What's actually going on: formatting money needs locale data, not string surgery",{"id":116,"html":117,"type":84},"b10","\u003Cp>Grouping separators, decimal marks, digit counts, and where the currency symbol sits relative to the number are not a style choice JavaScript can derive from an amount — they&#39;re a property of the \u003Cem>locale\u003C\u002Fem>, shipped as data in every browser:\u003C\u002Fp>",{"id":119,"head":120,"rows":123,"type":133},"b11",[121,122],"Locale","\u003Ccode>1234.5\u003C\u002Fcode> formatted as EUR",[124,127,130],[125,126],"\u003Ccode>en-US\u003C\u002Fcode>","\u003Ccode>€1,234.50\u003C\u002Fcode>",[128,129],"\u003Ccode>de-DE\u003C\u002Fcode>","\u003Ccode>1.234,50 €\u003C\u002Fcode>",[131,132],"\u003Ccode>fr-FR\u003C\u002Fcode>","\u003Ccode>1 234,50 €\u003C\u002Fcode>","table",{"id":135,"html":136,"type":84},"b12","\u003Cp>Same number, same currency, three genuinely different strings — none of them wrong, none of them producible by one regex. That&#39;s the part a hand-rolled formatter can&#39;t cover without reimplementing a chunk of \u003Ca href=\"https:\u002F\u002Fcldr.unicode.org\u002F\">CLDR\u003C\u002Fa>, the Unicode Consortium&#39;s locale database, which is exactly what \u003Ca href=\"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FJavaScript\u002FReference\u002FGlobal_Objects\u002FIntl\u002FNumberFormat\">\u003Ccode>Intl.NumberFormat\u003C\u002Fcode>\u003C\u002Fa> already did, in every JS engine, years ago:\u003C\u002Fp>",{"id":138,"code":139,"type":88,"language":89,"highlight":140},"b13","new Intl.NumberFormat('de-DE', {\n  style: 'currency',\n  currency: 'EUR',\n}).format(1234.5);\n\u002F\u002F \"1.234,50 €\"",[],{"id":142,"html":143,"type":84},"b14","\u003Cp>One constructor call, the correct separators, the correct symbol placement, for a locale the author of the regex has never had to think about. Swap the first argument and everything downstream follows:\u003C\u002Fp>",{"id":145,"code":146,"type":88,"language":89,"highlight":147},"b15","new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(1234.5);\n\u002F\u002F \"$1,234.50\"\n\nnew Intl.NumberFormat('fr-FR', { style: 'currency', currency: 'EUR' }).format(1234.5);\n\u002F\u002F \"1 234,50 €\"",[],{"id":149,"html":150,"type":84},"b16","\u003Cp>And the negative-number bug the regex needed a patch for is just... handled, because the formatter understands &quot;negative&quot; as a concept, not a leading character to tape on:\u003C\u002Fp>",{"id":152,"code":153,"type":88,"language":89,"highlight":154},"b17","new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(-1234.5);\n\u002F\u002F \"-$1,234.50\"",[],{"id":156,"html":157,"text":157,"type":100,"level":31},"b18","The options that cover the rest of the tickets",{"id":159,"html":160,"type":84},"b19","\u003Cp>Currency is the headline feature, but the same formatter answers most of the other tickets that end up patched into a regex over time:\u003C\u002Fp>",{"id":162,"html":163,"type":84},"b20","\u003Cp>\u003Cstrong>Compact numbers\u003C\u002Fstrong>, for a &quot;1.2K followers&quot; style display, without writing a \u003Ccode>&gt;= 1000 ? ... : ...\u003C\u002Fcode> ladder:\u003C\u002Fp>",{"id":165,"code":166,"type":88,"language":89,"highlight":167},"b21","new Intl.NumberFormat('en-US', { notation: 'compact' }).format(1234567);\n\u002F\u002F \"1.2M\"",[],{"id":169,"html":170,"type":84},"b22","\u003Cp>\u003Cstrong>Signed deltas\u003C\u002Fstrong>, for a dashboard showing &quot;+5.2%&quot; on a good day and &quot;-5.2%&quot; on a bad one — \u003Ccode>signDisplay: &#39;exceptZero&#39;\u003C\u002Fcode> adds the sign everywhere except an exact zero, which is usually the one case a hand-rolled version gets wrong first:\u003C\u002Fp>",{"id":172,"code":173,"type":88,"language":89,"highlight":174},"b23","new Intl.NumberFormat('en-US', { style: 'percent', signDisplay: 'exceptZero' }).format(0.052);\n\u002F\u002F \"+5%\"\nnew Intl.NumberFormat('en-US', { style: 'percent', signDisplay: 'exceptZero' }).format(0);\n\u002F\u002F \"0%\"",[],{"id":176,"html":177,"type":84},"b24","\u003Cp>\u003Cstrong>Units\u003C\u002Fstrong>, for &quot;12 km&quot; or &quot;3.5 hr&quot; without hand-building a suffix map:\u003C\u002Fp>",{"id":179,"code":180,"type":88,"language":89,"highlight":181},"b25","new Intl.NumberFormat('en-US', { style: 'unit', unit: 'kilometer' }).format(12);\n\u002F\u002F \"12 km\"",[],{"id":183,"html":184,"type":84},"b26","\u003Cp>All of it reads the user&#39;s own locale by default — \u003Ccode>new Intl.NumberFormat()\u003C\u002Fcode> with no locale argument uses the runtime&#39;s configured locale, which in a browser generally matches what \u003Ccode>navigator.language\u003C\u002Fcode> reports — so a page doesn&#39;t even need to hardcode which country it&#39;s formatting for.\u003C\u002Fp>",{"id":186,"html":187,"text":187,"type":100,"level":31},"b27","The one real gotcha: construct it once, not per row",{"id":189,"html":190,"type":84},"b28","\u003Cp>\u003Ccode>Intl.NumberFormat\u003C\u002Fcode> resolves a locale&#39;s rules when it&#39;s constructed, and that resolution is generally understood to be the heavier step compared to calling \u003Ccode>.format()\u003C\u002Fcode> on an already-built instance — which is why the common recommendation is to build the formatter once and reuse it, rather than constructing one per row inside a render loop:\u003C\u002Fp>",{"id":192,"code":193,"type":88,"language":89,"highlight":194},"b29","\u002F\u002F slow: builds a new formatter 10,000 times\nrows.map(r => new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(r.amount));",[],{"id":196,"code":197,"type":88,"language":89,"highlight":198},"b30","\u002F\u002F fast: one formatter, reused for every row\nconst money = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' });\nrows.map(r => money.format(r.amount));",[],{"id":200,"html":201,"type":84},"b31","\u003Cp>Same output, one constructor call instead of ten thousand. This is the detail that actually shows up in a profiler on a long table — not whether \u003Ccode>Intl.NumberFormat\u003C\u002Fcode> is &quot;slower than a regex&quot; in the abstract, but whether you&#39;re paying its setup cost once or on every single row.\u003C\u002Fp>",{"id":203,"html":204,"type":84},"b32","\u003C!-- playground:start -->",{"id":206,"html":207,"text":207,"type":100,"level":31},"b33","🎮 Try it yourself",{"id":209,"html":210,"type":84},"b34","\u003Cp>\u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fintl-numberformat-currency-formatting\u002Fplayground\">▶️ Open the interactive playground →\u003C\u002Fa>\u003C\u002Fstrong>\u003C\u002Fp>",{"id":212,"html":213,"type":84},"b35","\u003Cp>\u003Cem>Runs right in your browser — poke at it and watch the concept react live.\u003C\u002Fem>\u003C\u002Fp>",{"id":215,"html":216,"type":84},"b36","\u003C!-- playground:end -->",{"id":218,"html":219,"text":219,"type":100,"level":31},"b37","Delete the helper",{"id":221,"html":222,"type":84},"b38","\u003Cp>The regex-based \u003Ccode>formatMoney()\u003C\u002Fcode> isn&#39;t a bad instinct — it&#39;s solving a problem that feels like string manipulation because the output \u003Cem>is\u003C\u002Fem> a string. But the actual problem is locale data, and that&#39;s not something worth re-deriving one bug report at a time when the platform has shipped a correct, fast, built-in answer since before most teams&#39; codebases existed.\u003C\u002Fp>",{"id":224,"html":225,"type":84},"b39","\u003Cp>Next price, percentage, or &quot;12.3K views&quot; you&#39;re tempted to hand-format: what locale is it actually for, and have you checked what \u003Ccode>Intl.NumberFormat\u003C\u002Fcode> does with it before reaching for \u003Ccode>.toFixed()\u003C\u002Fcode>?\u003C\u002Fp>",{"id":227,"html":228,"type":84},"b40","\u003C!-- quiz:start -->",{"id":230,"html":231,"text":231,"type":100,"level":31},"b41","🧠 Test yourself",{"id":233,"html":234,"type":84},"b42","\u003Cp>Think it clicked? \u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fintl-numberformat-currency-formatting\u002Fquiz\">Take the 7-question quiz →\u003C\u002Fa>\u003C\u002Fstrong>\u003C\u002Fp>",{"id":236,"html":237,"type":84},"b43","\u003Cp>\u003Cem>Instant feedback, a hint on every question, and an explanation for each answer — right or wrong.\u003C\u002Fem>\u003C\u002Fp>",{"id":239,"html":240,"type":84},"b44","\u003C!-- quiz:end -->",{"id":242,"html":243,"type":84},"b45","\u003C!-- related:start -->",{"id":245,"html":246,"text":246,"type":100,"level":31},"b46","📚 Read next",{"id":248,"type":249,"items":250,"ordered":18},"b47","list",[251,252,253],"\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Ftemporal-api-date-mutation-trap\">addDays() Mutated a Date Three Components Away From Where I Called It\u003C\u002Fa>","\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fintl-segmenter-grapheme-word-sentence\">You&#39;re truncating bios with \u003Ccode>.slice()\u003C\u002Fcode>. \u003Ccode>Intl.Segmenter\u003C\u002Fcode> knows where the emoji actually end.\u003C\u002Fa>","\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fintl-relativetimeformat-relative-dates\">You&#39;re installing date-fns for &#39;X hours ago&#39;. \u003Ccode>Intl.RelativeTimeFormat\u003C\u002Fcode> does it natively.\u003C\u002Fa>",{"id":255,"html":256,"type":84},"b48","\u003C!-- related:end -->",{"id":258,"type":259},"b49","divider",{"id":261,"html":262,"type":84},"b50","\u003Cp>🚀 \u003Cstrong>Want more like this?\u003C\u002Fstrong> Every guide, playground, and quiz lives on \u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002F\">bestpractic.org\u003C\u002Fa>\u003C\u002Fstrong> — open it and \u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002F\">sign up free\u003C\u002Fa>\u003C\u002Fstrong> so the next one finds you.\u003C\u002Fp>",{"id":264,"html":265,"type":84},"b51","\u003Cp>\u003Cem>Thanks for reading! Let&#39;s stay connected:\u003C\u002Fem>\u003C\u002Fp>",{"id":267,"type":249,"items":268,"ordered":18},"b52",[269,270,271],"⭐ \u003Cstrong>GitHub\u003C\u002Fstrong> — follow me and star the projects: \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fparsajiravand\">github.com\u002Fparsajiravand\u003C\u002Fa>","💬 \u003Cstrong>Discord\u003C\u002Fstrong> — join the frontend best-practices community: \u003Ca href=\"https:\u002F\u002Fdiscord.gg\u002Fd9KRhuAwQ\">discord.gg\u002Fd9KRhuAwQ\u003C\u002Fa>","📸 \u003Cstrong>Instagram\u003C\u002Fstrong> — frontend best practices, daily: \u003Ca href=\"https:\u002F\u002Fwww.instagram.com\u002Fbestpractice___\u002F\">@bestpractice___\u003C\u002Fa>","Open any finance dashboard's test suite and there's a decent chance you'll find a function like this, usually written once, two years ago, by someone who is no longer on the team:\n\n```js\nfunction formatMoney(amount) {\n  return '$' + amount.toFixed(2).replace(\u002F\\B(?=(\\d{3})+(?!\\d))\u002Fg, ',');\n}\n```\n\nIt passes its tests. `formatMoney(1234.5)` returns `\"$1,234.50\"`, which is exactly what the ticket asked for. Nobody has touched it since, because it works — until a refund comes through as a negative number, or a stakeholder asks for a per-unit cost that needs four decimal places, or the company opens an EU region and legal asks why every price shows up with a comma where European customers expect a period.\n\nNone of those are edge cases. They're Tuesday.\n\n## The fix everyone reaches for: patch the regex\n\nThe usual response to \"it broke for amount X\" is to patch the one case that broke:\n\n```js\nfunction formatMoney(amount) {\n  const sign = amount \u003C 0 ? '-' : '';\n  const fixed = Math.abs(amount).toFixed(2);\n  return sign + '$' + fixed.replace(\u002F\\B(?=(\\d{3})+(?!\\d))\u002Fg, ',');\n}\n```\n\nThat fixes negatives. It does not fix the next report, four decimal places: `formatMoney(1234.5678)` still runs the same comma-grouping regex across the *entire* string, digits after the decimal point included — so instead of `\"$1,234.5678\"` it can silently insert a comma inside the fractional part too, because the regex has no idea where the decimal point is relative to its own grouping. And none of this touches the actual question underneath the ticket: a German user doesn't want `\"1,234.50\"` at all. They want `\"1.234,50\"` — comma and period swapped — followed by a `€` with a non-breaking space in front of it, not a `$` in front with no space. A regex built around US formatting conventions can't produce that output by patching cases one at a time, because it never encoded \"how do numbers look in Germany\" in the first place. There's no case to patch for that; the whole function is missing the concept.\n\n## What's actually going on: formatting money needs locale data, not string surgery\n\nGrouping separators, decimal marks, digit counts, and where the currency symbol sits relative to the number are not a style choice JavaScript can derive from an amount — they're a property of the *locale*, shipped as data in every browser:\n\n| Locale | `1234.5` formatted as EUR |\n|---|---|\n| `en-US` | `€1,234.50` |\n| `de-DE` | `1.234,50 €` |\n| `fr-FR` | `1 234,50 €` |\n\nSame number, same currency, three genuinely different strings — none of them wrong, none of them producible by one regex. That's the part a hand-rolled formatter can't cover without reimplementing a chunk of [CLDR](https:\u002F\u002Fcldr.unicode.org\u002F), the Unicode Consortium's locale database, which is exactly what [`Intl.NumberFormat`](https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FJavaScript\u002FReference\u002FGlobal_Objects\u002FIntl\u002FNumberFormat) already did, in every JS engine, years ago:\n\n```js\nnew Intl.NumberFormat('de-DE', {\n  style: 'currency',\n  currency: 'EUR',\n}).format(1234.5);\n\u002F\u002F \"1.234,50 €\"\n```\n\nOne constructor call, the correct separators, the correct symbol placement, for a locale the author of the regex has never had to think about. Swap the first argument and everything downstream follows:\n\n```js\nnew Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(1234.5);\n\u002F\u002F \"$1,234.50\"\n\nnew Intl.NumberFormat('fr-FR', { style: 'currency', currency: 'EUR' }).format(1234.5);\n\u002F\u002F \"1 234,50 €\"\n```\n\nAnd the negative-number bug the regex needed a patch for is just... handled, because the formatter understands \"negative\" as a concept, not a leading character to tape on:\n\n```js\nnew Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(-1234.5);\n\u002F\u002F \"-$1,234.50\"\n```\n\n## The options that cover the rest of the tickets\n\nCurrency is the headline feature, but the same formatter answers most of the other tickets that end up patched into a regex over time:\n\n**Compact numbers**, for a \"1.2K followers\" style display, without writing a `>= 1000 ? ... : ...` ladder:\n\n```js\nnew Intl.NumberFormat('en-US', { notation: 'compact' }).format(1234567);\n\u002F\u002F \"1.2M\"\n```\n\n**Signed deltas**, for a dashboard showing \"+5.2%\" on a good day and \"-5.2%\" on a bad one — `signDisplay: 'exceptZero'` adds the sign everywhere except an exact zero, which is usually the one case a hand-rolled version gets wrong first:\n\n```js\nnew Intl.NumberFormat('en-US', { style: 'percent', signDisplay: 'exceptZero' }).format(0.052);\n\u002F\u002F \"+5%\"\nnew Intl.NumberFormat('en-US', { style: 'percent', signDisplay: 'exceptZero' }).format(0);\n\u002F\u002F \"0%\"\n```\n\n**Units**, for \"12 km\" or \"3.5 hr\" without hand-building a suffix map:\n\n```js\nnew Intl.NumberFormat('en-US', { style: 'unit', unit: 'kilometer' }).format(12);\n\u002F\u002F \"12 km\"\n```\n\nAll of it reads the user's own locale by default — `new Intl.NumberFormat()` with no locale argument uses the runtime's configured locale, which in a browser generally matches what `navigator.language` reports — so a page doesn't even need to hardcode which country it's formatting for.\n\n## The one real gotcha: construct it once, not per row\n\n`Intl.NumberFormat` resolves a locale's rules when it's constructed, and that resolution is generally understood to be the heavier step compared to calling `.format()` on an already-built instance — which is why the common recommendation is to build the formatter once and reuse it, rather than constructing one per row inside a render loop:\n\n```js\n\u002F\u002F slow: builds a new formatter 10,000 times\nrows.map(r => new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(r.amount));\n```\n\n```js\n\u002F\u002F fast: one formatter, reused for every row\nconst money = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' });\nrows.map(r => money.format(r.amount));\n```\n\nSame output, one constructor call instead of ten thousand. This is the detail that actually shows up in a profiler on a long table — not whether `Intl.NumberFormat` is \"slower than a regex\" in the abstract, but whether you're paying its setup cost once or on every single row.\n\n\u003C!-- playground:start -->\n\n## 🎮 Try it yourself\n\n**[▶️ Open the interactive playground →](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fintl-numberformat-currency-formatting\u002Fplayground)**\n\n_Runs right in your browser — poke at it and watch the concept react live._\n\n\u003C!-- playground:end -->\n\n## Delete the helper\n\nThe regex-based `formatMoney()` isn't a bad instinct — it's solving a problem that feels like string manipulation because the output *is* a string. But the actual problem is locale data, and that's not something worth re-deriving one bug report at a time when the platform has shipped a correct, fast, built-in answer since before most teams' codebases existed.\n\nNext price, percentage, or \"12.3K views\" you're tempted to hand-format: what locale is it actually for, and have you checked what `Intl.NumberFormat` does with it before reaching for `.toFixed()`?\n\n\u003C!-- quiz:start -->\n\n## 🧠 Test yourself\n\nThink it clicked? **[Take the 7-question quiz →](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fintl-numberformat-currency-formatting\u002Fquiz)**\n\n_Instant feedback, a hint on every question, and an explanation for each answer — right or wrong._\n\n\u003C!-- quiz:end -->\n\n\u003C!-- related:start -->\n\n## 📚 Read next\n\n- [addDays() Mutated a Date Three Components Away From Where I Called It](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Ftemporal-api-date-mutation-trap)\n- [You're truncating bios with `.slice()`. `Intl.Segmenter` knows where the emoji actually end.](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fintl-segmenter-grapheme-word-sentence)\n- [You're installing date-fns for 'X hours ago'. `Intl.RelativeTimeFormat` does it natively.](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fintl-relativetimeformat-relative-dates)\n\n\u003C!-- related:end -->\n\n---\n\n🚀 **Want more like this?** Every guide, playground, and quiz lives on **[bestpractic.org](https:\u002F\u002Fbestpractic.org\u002F)** — open it and **[sign up free](https:\u002F\u002Fbestpractic.org\u002F)** so the next one finds you.\n\n*Thanks for reading! Let's stay connected:*\n\n- ⭐ **GitHub** — follow me and star the projects: [github.com\u002Fparsajiravand](https:\u002F\u002Fgithub.com\u002Fparsajiravand)\n- 💬 **Discord** — join the frontend best-practices community: [discord.gg\u002Fd9KRhuAwQ](https:\u002F\u002Fdiscord.gg\u002Fd9KRhuAwQ)\n- 📸 **Instagram** — frontend best practices, daily: [@bestpractice___](https:\u002F\u002Fwww.instagram.com\u002Fbestpractice___\u002F)",{"title":47,"canonical":274,"description":275},"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fintl-numberformat-currency-formatting","A hand-rolled formatMoney() with toFixed() and a comma regex looks fine in a US demo and quietly breaks the moment a real user, a negative balance, or a non-dollar locale shows up.","01a13e2e-f495-74ab-b10e-81856b0156e5",{"id":278,"locked":18},"01a13e2e-f4ea-735d-a54d-a2871a19895e",[280],{"id":281,"slug":46,"title":282,"_count":283},"01a13e2e-f51e-7215-b0e4-cce60ed91ae5","Intl.NumberFormat: formatting money without the regex",{"questions":284},7,[286],{"locale":13,"slug":46},{"id":281,"slug":46,"title":282,"_count":288,"questionCount":284},{"questions":284},[290,292,294,298,302,306,310,312,316,320,322,325],{"slug":67,"name":68,"articles":291},148,{"slug":61,"name":65,"articles":293},121,{"slug":295,"name":296,"articles":297},"frontend","Frontend",87,{"slug":299,"name":300,"articles":301},"tutorial","Tutorial",57,{"slug":303,"name":304,"articles":305},"css","Css",44,{"slug":307,"name":308,"articles":309},"performance","Performance",24,{"slug":70,"name":71,"articles":311},20,{"slug":313,"name":314,"articles":315},"react","React",18,{"slug":317,"name":318,"articles":319},"browser","Browser",14,{"slug":73,"name":74,"articles":321},12,{"slug":323,"name":324,"articles":321},"node","Node",{"slug":326,"name":327,"articles":328},"accessibility","Accessibility",10,[],{"items":331,"meta":463},[332,352,364,385,403,426,444],{"id":333,"slug":334,"title":335,"subtitle":48,"excerpt":336,"coverUrl":337,"locale":13,"readingMinutes":319,"publishedAt":338,"viewCount":339,"likeCount":19,"commentCount":19,"author":340,"vertical":341,"topic":342,"tags":345,"_count":350,"playground":351,"hasQuiz":17,"hasPlayground":17},"01a149cb-68e6-773f-bbbb-fce803732119","nextjs-weekly-instant-navigations-partial-prefetching","Next.js 16.3 Instant Navigations: How Partial Prefetching Works","Next.js 16.3 shipped Instant Navigations: Partial Prefetching plus the App Shell. Learn the model, the Suspense gotcha, and a copy-paste cheat sheet.","\u002Fmedia\u002Fcovers\u002Fnextjs-weekly-instant-navigations-partial-prefetching.png","2026-10-20T06:10:59.936Z",64,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":343,"name":344},"nextjs","Nextjs",[346,347,348,349],{"slug":343,"name":344,"color":48},{"slug":67,"name":68,"color":48},{"slug":299,"name":300,"color":48},{"slug":61,"name":65,"color":48},{"assessments":76},{"slug":334},{"id":45,"slug":46,"title":47,"subtitle":48,"excerpt":49,"coverUrl":50,"locale":13,"readingMinutes":51,"publishedAt":52,"viewCount":353,"likeCount":19,"commentCount":19,"author":354,"vertical":355,"topic":356,"tags":357,"_count":362,"playground":363,"hasQuiz":17,"hasPlayground":17},48,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[358,359,360,361],{"slug":61,"name":65,"color":48},{"slug":67,"name":68,"color":48},{"slug":70,"name":71,"color":48},{"slug":73,"name":74,"color":48},{"assessments":76},{"slug":46},{"id":365,"slug":366,"title":367,"subtitle":48,"excerpt":368,"coverUrl":369,"locale":13,"readingMinutes":370,"publishedAt":371,"viewCount":372,"likeCount":19,"commentCount":19,"author":373,"vertical":374,"topic":375,"tags":378,"_count":383,"playground":384,"hasQuiz":17,"hasPlayground":17},"01a0f781-ba1b-7528-8bc8-b9c9fe1b0e38","vue-weekly-reactivity-batch-watcher-throws","Vue 3.6: When a Watcher Throws, Your Other Watchers Stall","A Vue 3.6 reactivity regression let a throwing watcher silently stall its siblings. The exact flush() mechanism, the fix, and a copy-paste cheat sheet.","\u002Fmedia\u002Fcovers\u002Fvue-weekly-reactivity-batch-watcher-throws.png",15,"2026-10-19T06:10:59.563Z",116,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":376,"name":377},"vue","Vue",[379,380,381,382],{"slug":376,"name":377,"color":48},{"slug":61,"name":65,"color":48},{"slug":299,"name":300,"color":48},{"slug":67,"name":68,"color":48},{"assessments":76},{"slug":366},{"id":386,"slug":387,"title":388,"subtitle":48,"excerpt":389,"coverUrl":390,"locale":13,"readingMinutes":51,"publishedAt":391,"viewCount":392,"likeCount":19,"commentCount":19,"author":393,"vertical":394,"topic":395,"tags":396,"_count":401,"playground":402,"hasQuiz":17,"hasPlayground":17},"01a13908-8b6a-71f8-9fbc-606a126441c0","layout-thrashing-forced-reflow","Your 'Expand All' Button Is Quietly Causing Layout Thrashing","An 'expand all' click that stutters isn't a slow CPU — it's layout thrashing, a loop that forces the browser to recalculate layout once per row instead of once per click. Here's how to spot it and the two-line fix.","\u002Fmedia\u002Fcovers\u002Flayout-thrashing-forced-reflow.png","2026-10-19T06:10:27.840Z",95,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[397,398,399,400],{"slug":61,"name":65,"color":48},{"slug":307,"name":308,"color":48},{"slug":67,"name":68,"color":48},{"slug":317,"name":318,"color":48},{"assessments":76},{"slug":387},{"id":404,"slug":405,"title":406,"subtitle":48,"excerpt":407,"coverUrl":408,"locale":13,"readingMinutes":409,"publishedAt":410,"viewCount":411,"likeCount":19,"commentCount":19,"author":412,"vertical":413,"topic":414,"tags":417,"_count":424,"playground":425,"hasQuiz":17,"hasPlayground":17},"01a143f6-4be7-7006-8c06-e84ad4e83026","nuxt-weekly-usesession-sealed-sessions","Nuxt 4.6 useSession: Server Sessions Without a Database","Nuxt 4.6 ships useSession and runtimeConfig.appSecret — real, sealed-cookie sessions with no database or session store. How it works, plus a cheat sheet.","\u002Fmedia\u002Fcovers\u002Fnuxt-weekly-usesession-sealed-sessions.png",13,"2026-10-18T10:15:34.613Z",152,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":415,"name":416},"nuxt","Nuxt",[418,419,422,423],{"slug":415,"name":416,"color":48},{"slug":420,"name":421,"color":48},"security","Security",{"slug":61,"name":65,"color":48},{"slug":299,"name":300,"color":48},{"assessments":76},{"slug":405},{"id":427,"slug":428,"title":429,"subtitle":48,"excerpt":430,"coverUrl":431,"locale":13,"readingMinutes":370,"publishedAt":432,"viewCount":433,"likeCount":19,"commentCount":19,"author":434,"vertical":435,"topic":436,"tags":437,"_count":442,"playground":443,"hasQuiz":17,"hasPlayground":17},"01a13e2e-d5ae-707f-a6c5-726f2ed01ab0","react-weekly-useeffectevent-stale-closures","React 19.2 useEffectEvent: Why It Won't Fix Your Deps Array","React's useEffectEvent (stable in 19.2) kills stale closures in Effects without a ref trick — but it's not a deps-array shortcut. Guide + cheat sheet.","\u002Fmedia\u002Fcovers\u002Freact-weekly-useeffectevent-stale-closures.png","2026-10-17T12:20:57.469Z",99,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":313,"name":314},[438,439,440,441],{"slug":313,"name":314,"color":48},{"slug":61,"name":65,"color":48},{"slug":299,"name":300,"color":48},{"slug":67,"name":68,"color":48},{"assessments":76},{"slug":428},{"id":445,"slug":446,"title":447,"subtitle":48,"excerpt":448,"coverUrl":449,"locale":13,"readingMinutes":51,"publishedAt":450,"viewCount":451,"likeCount":19,"commentCount":19,"author":452,"vertical":453,"topic":454,"tags":456,"_count":461,"playground":462,"hasQuiz":17,"hasPlayground":17},"01a12ebb-c68a-7460-98eb-bc3859d715a0","inferred-type-predicates-filter-boolean","Inferred Type Predicates Won't Save filter(Boolean). Here's What Will.","TypeScript 5.5 added inferred type predicates, and the internet decided that meant filter(Boolean) finally narrows. It doesn't. Here's exactly what the feature fixed, why the mix-up happened, and the one-line pattern that actually gets you type-safe filtering.","\u002Fmedia\u002Fcovers\u002Finferred-type-predicates-filter-boolean.png","2026-10-17T12:17:17.948Z",146,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":70,"name":455},"TypeScript",[457,458,459,460],{"slug":70,"name":71,"color":48},{"slug":61,"name":65,"color":48},{"slug":73,"name":74,"color":48},{"slug":67,"name":68,"color":48},{"assessments":76},{"slug":446},{"page":76,"perPage":284,"total":293,"totalPages":315},"\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-func\">formatMoney\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>amount\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> \u003Cspan class=\"shj-str\">'$'\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">+\u003C\u002Fspan> amount\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">toFixed\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-num\">2\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">replace\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">\\B\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">(?=(\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">\\d\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">{3})\u003C\u002Fspan>\u003Cspan class=\"shj-var\">+\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">(?!\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">\\d\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">))\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">g\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-str\">','\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-func\">formatMoney\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>amount\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> sign \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> amount \u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan> \u003Cspan class=\"shj-num\">0\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">?\u003C\u002Fspan> \u003Cspan class=\"shj-str\">'-'\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">''\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> fixed \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Math\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">abs\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>amount\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">toFixed\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-num\">2\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> sign \u003Cspan class=\"shj-oper\">+\u003C\u002Fspan> \u003Cspan class=\"shj-str\">'$'\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">+\u003C\u002Fspan> fixed\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">replace\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">\\B\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">(?=(\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">\\d\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">{3})\u003C\u002Fspan>\u003Cspan class=\"shj-var\">+\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">(?!\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">\\d\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">))\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">g\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-str\">','\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">new\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Intl\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-class\">NumberFormat\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">'de-DE'\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  style\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">'currency'\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan>\n  currency\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">'EUR'\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan>\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">format\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-num\">1234.5\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-cmnt\">\u002F\u002F \"1.234,50 €\"\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">new\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Intl\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-class\">NumberFormat\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">'en-US'\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> style\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">'currency'\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> currency\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">'USD'\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">format\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-num\">1234.5\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-cmnt\">\u002F\u002F \"$1,234.50\"\n\u003C\u002Fspan>\n\u003Cspan class=\"shj-kwd\">new\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Intl\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-class\">NumberFormat\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">'fr-FR'\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> style\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">'currency'\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> currency\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">'EUR'\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">format\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-num\">1234.5\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-cmnt\">\u002F\u002F \"1 234,50 €\"\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">new\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Intl\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-class\">NumberFormat\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">'en-US'\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> style\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">'currency'\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> currency\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">'USD'\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">format\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">-\u003C\u002Fspan>\u003Cspan class=\"shj-num\">1234.5\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-cmnt\">\u002F\u002F \"-$1,234.50\"\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">new\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Intl\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-class\">NumberFormat\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">'en-US'\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> notation\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">'compact'\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">format\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-num\">1234567\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-cmnt\">\u002F\u002F \"1.2M\"\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">new\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Intl\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-class\">NumberFormat\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">'en-US'\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> style\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">'percent'\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> signDisplay\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">'exceptZero'\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">format\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-num\">0.052\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-cmnt\">\u002F\u002F \"+5%\"\n\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">new\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Intl\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-class\">NumberFormat\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">'en-US'\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> style\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">'percent'\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> signDisplay\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">'exceptZero'\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">format\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-num\">0\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-cmnt\">\u002F\u002F \"0%\"\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">new\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Intl\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-class\">NumberFormat\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">'en-US'\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> style\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">'unit'\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> unit\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">'kilometer'\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">format\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-num\">12\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-cmnt\">\u002F\u002F \"12 km\"\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-cmnt\">\u002F\u002F slow: builds a new formatter 10,000 times\n\u003C\u002Fspan>rows\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">map\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>r \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">new\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Intl\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-class\">NumberFormat\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">'en-US'\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> style\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">'currency'\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> currency\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">'USD'\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">format\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>r\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>amount\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-cmnt\">\u002F\u002F fast: one formatter, reused for every row\n\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> money \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">new\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Intl\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-class\">NumberFormat\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">'en-US'\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> style\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">'currency'\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> currency\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">'USD'\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\nrows\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">map\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>r \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> money\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">format\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>r\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>amount\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>",{"locked":18,"total":19,"comments":475},[]]