Understanding formatToParts in JavaScript

Master the art of styling formatted numbers by accessing individual components like currency symbols, decimal points, and grouping separators through JavaScript's Intl.NumberFormat API.

The Problem with Formatted Strings

When you work with internationalized numbers in JavaScript, the Intl.NumberFormat API provides two ways to generate formatted output. The format() method returns a complete, localized string like "$1,234.56" or "1.234,56 €". This works perfectly for simple display scenarios where you need to show a formatted value to users. However, this approach hits a wall when you need to customize the appearance of individual components within that formatted string.

Modern web applications increasingly require granular control over how formatted content appears. A pricing display might call for the currency symbol in a bold, accent-colored font while the decimal portion appears smaller and lighter. A financial dashboard might need to highlight positive and negative values differently. An accessibility-focused interface might require adding ARIA labels to specific components. None of these requirements can be met when you only have an opaque formatted string to work with.

The formatToParts() method exists precisely to address these limitations. Instead of returning a single string, formatToParts() returns an array of objects, where each object describes one part of the formatted output. Each part includes a type property identifying what the part represents (currency symbol, integer digits, decimal separator, and so on) and a value property containing the actual string content. This structured output enables you to process, style, or manipulate each component independently while still benefiting from the full power of the Intl API's locale-aware formatting.

This approach mirrors a pattern that appears across the entire Intl namespace. The DateTimeFormat, ListFormat, RelativeTimeFormat, and newer formatters all provide formatToParts() methods with consistent behavior. Understanding this method positions you to work effectively with all of JavaScript's internationalization formatting capabilities.

When building custom web applications that serve international audiences, proper number formatting becomes essential for creating professional, locally-aware user experiences.

format() vs formatToParts() Comparison
1const formatter = new Intl.NumberFormat("en-US", {2 style: "currency",3 currency: "USD"4});5 6const amount = 1234.56;7 8// format() returns a simple string9console.log(formatter.format(amount));10// Output: "$1,234.56"11 12// formatToParts() returns structured data13console.log(formatter.formatToParts(amount));14// Output: [15// { type: "currency", value: "$" },16// { type: "integer", value: "1" },17// { type: "group", value: "," },18// { type: "integer", value: "234" },19// { type: "decimal", value: "." },20// { type: "fraction", value: "56" }21// ]

How formatToParts Works

The formatToParts() method follows the same pattern as format(). You create an Intl.NumberFormat instance with your desired locale and options, then call formatToParts() instead of format(), passing the number you want to format. The method returns an array of part objects rather than a string.

Understanding the available part types is essential for writing effective formatting code. The MDN Web Docs provides comprehensive documentation on all available part types and their usage patterns.

For currency formatting, the currency type represents the currency symbol, code, or name depending on your currencyDisplay option. The integer type represents whole number digits before any decimal point, and the group type represents the thousands separator, which varies by locale ("," in en-US, "." in de-DE, and other characters in different locales). The decimal type represents the decimal separator, while the fraction type represents digits after the decimal point.

For scientific or compact notation, additional part types appear. The compact type represents compact notation markers like "M" for millions or "K" for thousands. The plusSign and minusSign types represent explicit positive and negative signs. The literal type captures any text that appears in the formatted output but doesn't fall into other categories, such as spaces between components or punctuation.

NumberFormat Part Types
TypeDescriptionExample Value
currencyCurrency symbol, code, or name$ / USD / US Dollar
integerWhole number digits before decimal1, 234
groupThousands separator, or .
decimalDecimal separator. or ,
fractionDigits after decimal point56
percentSignPercent symbol%
unitUnit stringl or litres
compactCompact notation markerM or K
minusSignNegative sign-
plusSignPositive sign+
literalSpacing, punctuation, other textspace or ,

Styling Formatted Numbers

The most common reason to use formatToParts() is applying different visual styles to different components of a formatted number. A price display might show the currency symbol in a smaller, muted style while keeping the main numeric value prominent and bold. This pattern is particularly valuable for e-commerce platforms where pricing clarity directly impacts conversion rates.

The basic pattern involves iterating over the parts array, checking each part's type, and wrapping specific types in appropriate markup. By processing each part individually, you can create visually hierarchical displays where users can quickly scan the main numeric value while still having access to the complete formatted price.

A more sophisticated implementation might apply different styles based on value ranges, highlight negative values in red, or create conditional formatting for various business requirements. This attention to formatting detail demonstrates the kind of polished user experience that sets professional web applications apart from basic implementations.

Styling Currency Components
1function formatPrice(value, currency = "USD") {2 const formatter = new Intl.NumberFormat("en-US", {3 style: "currency",4 currency: currency5 });6 7 const parts = formatter.formatToParts(value);8 9 return parts10 .map(part => {11 if (part.type === "currency") {12 return `<span class="text-sm text-gray-500 font-medium">${part.value}</span>`;13 }14 if (part.type === "integer" || part.type === "group") {15 return `<span class="text-2xl font-bold">${part.value}</span>`;16 }17 if (part.type === "decimal" || part.type === "fraction") {18 return `<span class="text-sm text-gray-500">${part.value}</span>`;19 }20 return part.value;21 })22 .join("");23}24 25// Creates: $1,234.56 with hierarchical styling

Locale-Aware Formatting Across Regions

One of the most powerful aspects of using formatToParts() is that it automatically handles locale-specific formatting differences. Different locales use different decimal separators, grouping patterns, currency placements, and other formatting conventions. This automatic handling becomes crucial when building multilingual web applications that serve international users--you don't need separate formatting logic for each locale.

Consider how US dollars and euros are formatted differently. The German format places the currency symbol after the number with a space, uses periods for grouping and commas for decimals. Japanese currency formatting follows yet another convention. Indian numbering systems use a unique grouping pattern with lakhs and crores.

Your styling code doesn't need to change to handle these differences. The formatter produces the appropriate parts, and your processing logic works the same way regardless of part ordering or separator character. This consistency makes it straightforward to maintain a unified codebase while supporting diverse regional preferences. The Lingo.dev guide provides additional patterns for handling these scenarios effectively.

Different Locales, Different Formats
1// US Dollar: $1,234.562const usdFormatter = new Intl.NumberFormat("en-US", {3 style: "currency",4 currency: "USD"5});6// Parts: currency($), integer(1), group(,), integer(234), decimal(.), fraction(56)7 8// Euro (German): 1.234,56 €9const eurFormatter = new Intl.NumberFormat("de-DE", {10 style: "currency",11 currency: "EUR"12});13// Parts: integer(1), group(.), integer(234), decimal(,), fraction(56), literal( ), currency(€)14 15// Japanese Yen: ¥123,45616const jpyFormatter = new Intl.NumberFormat("ja-JP", {17 style: "currency",18 currency: "JPY"19});20// Parts: currency(¥), integer(123), group(,), integer(456)

Building Framework Components

Modern JavaScript frameworks make it straightforward to integrate formatToParts() into reusable components. The key is processing the parts array and rendering appropriate elements for each type. This approach is essential when building React applications or Vue.js applications that require precise control over formatted number presentation.

Creating reusable formatters as custom hooks or utility functions allows you to centralize formatting logic across your application, ensuring consistency and making future updates easier to manage. A well-designed currency display component can accept styling props to customize appearance, making it flexible enough to handle various design requirements without code duplication.

By extracting formatting logic into composable pieces, you create a foundation that scales across your application. Whether you're building a pricing page, a financial dashboard, or an invoice system, the same core patterns apply.

React CurrencyDisplay Component
1function CurrencyDisplay({ value, currency = "USD", locale = "en-US" }) {2 const formatter = new Intl.NumberFormat(locale, {3 style: "currency",4 currency: currency5 });6 7 const parts = formatter.formatToParts(value);8 9 return (10 <span className="currency-display">11 {parts.map((part, index) => {12 switch (part.type) {13 case "currency":14 return (15 <span key={index} className="currency-symbol text-gray-500 font-medium">16 {part.value}17 </span>18 );19 case "integer":20 case "group":21 return (22 <span key={index} className="integer-part text-2xl font-bold">23 {part.value}24 </span>25 );26 case "decimal":27 return (28 <span key={index} className="decimal-point text-gray-400">29 {part.value}30 </span>31 );32 case "fraction":33 return (34 <span key={index} className="fraction-part text-sm text-gray-500">35 {part.value}36 </span>37 );38 default:39 return <span key={index}>{part.value}</span>;40 }41 })}42 </span>43 );44}

Performance Considerations

Understanding the performance characteristics of format() versus formatToParts() helps you make appropriate trade-offs in your applications.

The format() method creates and returns a single string, which is a straightforward operation with minimal memory allocation. The formatToParts() method must construct an array of objects, each with a type and value property, which involves more memory allocation and processing overhead.

For typical applications that format numbers occasionally, the performance difference is imperceptible. Both methods complete in microseconds, and the human eye cannot perceive any timing difference. However, in high-performance scenarios--like real-time dashboards or data visualization libraries--choosing the right method can have measurable impact.

The key optimization is reusing formatter instances rather than creating new ones for each operation. Creating an Intl.NumberFormat instance involves parsing locale data and initializing formatting rules, which has measurable overhead. By caching formatters in a Map keyed by locale and currency, you significantly improve performance for repeated formatting operations. This pattern is essential when processing large datasets or rendering many formatted values simultaneously.

Optimization: Reuse Formatters
1// Optimization: Reuse formatters for repeated formatting2const priceFormatters = new Map();3 4function getFormatter(locale, currency) {5 const key = `${locale}-${currency}`;6 if (!priceFormatters.has(key)) {7 priceFormatters.set(key, new Intl.NumberFormat(locale, {8 style: "currency",9 currency: currency10 }));11 }12 return priceFormatters.get(key);13}14 15// When you need plain formatting, use format()16function displayPrice(value, currency) {17 const formatter = getFormatter("en-US", currency);18 return formatter.format(value);19}20 21// When you need styled output, use formatToParts()22function styledPrice(value, currency) {23 const formatter = getFormatter("en-US", currency);24 const parts = formatter.formatToParts(value);25 // Process and style parts...26}

Advanced Patterns

Accessibility Enhancements

Screen readers benefit from structured accessibility information that formatToParts() makes possible. By extracting the numeric value separately and adding appropriate ARIA labels, you can ensure that visually styled numbers remain accessible to all users. This attention to accessibility is a hallmark of professional web development that prioritizes inclusive user experiences.

Beyond basic accessibility, the structured output from formatToParts() enables sophisticated patterns like dynamic styling based on value ranges, conditional formatting for positive and negative values, and integration with compact notation for large numbers. Building a comprehensive number formatting utility library using these patterns provides a foundation for consistent, maintainable formatting across your entire application.

Compact notation displays large numbers in abbreviated form (like "1.2M" or "500K"), and formatToParts() helps you style these compact markers differently from the main numeric value, creating visual hierarchy in dashboards and reports that display metrics at scale.

Accessible Price Formatting
1function formatAccessiblePrice(value, currency = "USD") {2 const formatter = new Intl.NumberFormat("en-US", {3 style: "currency",4 currency: currency5 });6 7 const parts = formatter.formatToParts(value);8 const formatted = parts.map(p => p.value).join("");9 10 // Add ARIA label for screen readers11 return {12 html: `<span aria-label="${value} ${currency}">${formatted}</span>`,13 formatted: formatted14 };15}

Summary

The formatToParts() method unlocks powerful capabilities for working with internationalized number formatting in JavaScript. By providing structured access to each component of a formatted number, it enables styling, accessibility enhancements, and integration with component frameworks that the simple format() method cannot support.

Key takeaways:

  • formatToParts() returns an array of objects with type and value properties
  • Part types include currency, integer, group, decimal, fraction, and more
  • Different locales produce different part orderings and separator characters
  • Your styling logic works consistently across all locales
  • Use format() for simple display, formatToParts() when you need customization

Mastering these patterns positions you to build sophisticated web applications that deliver polished, internationally-aware user experiences. Whether you're building e-commerce platforms, financial dashboards, or enterprise applications, proper number formatting demonstrates the attention to detail that distinguishes professional software.

Explore our web development services to learn how we can help you implement these patterns in your projects.

Frequently Asked Questions

What is the difference between format() and formatToParts()?

format() returns a single formatted string like "$1,234.56", while formatToParts() returns an array of objects describing each component. Use format() for simple display, formatToParts() when you need to style or manipulate individual parts.

Does formatToParts() work with all locales?

Yes, formatToParts() works with all locales supported by Intl.NumberFormat. It automatically handles locale-specific conventions like different decimal separators, grouping patterns, and currency placements.

What part types are available?

Common types include currency, integer, group, decimal, fraction, percentSign, unit, compact, minusSign, plusSign, and literal. Some types only appear in specific formatting contexts like scientific notation or percentage formatting.

Is formatToParts() slower than format()?

formatToParts() has slightly more overhead because it creates an array of objects instead of a string. For typical web applications, this difference is negligible. Choose based on your functional requirements rather than performance concerns.

Can I use formatToParts() with React components?

Absolutely. Many applications use formatToParts() in React, Vue, and other frameworks to create styled number display components that render differently for currency symbols, decimal portions, and other parts.

Ready to Build Sophisticated Web Applications?

Our team specializes in creating performant, accessible, and internationally-aware web applications using modern JavaScript techniques.

Sources

  1. MDN Web Docs: Intl.NumberFormat.prototype.formatToParts() - Official documentation for syntax, parameters, and return types
  2. MDN Web Docs: Intl.NumberFormat - Core NumberFormat API reference
  3. Lingo.dev: JavaScript i18n guide - Practical styling, HTML generation, and framework integration patterns