How to get individual parts of a formatted date in JavaScript

Use formatToParts() to access each component of a locale-formatted date separately

Introduction

The format() method returns a complete formatted string like "January 15, 2025" or "15/01/2025". This works well for simple display, but you cannot style individual parts differently. You cannot make the month name bold, color the year differently, or apply custom markup to specific components.

JavaScript provides the formatToParts() method to solve this problem. Instead of returning a single string, it returns an array of objects, each representing one part of the formatted date. Each part has a type like month, day, or year, and a value like January, 15, or 2025. You can then process these parts to apply custom styling, build complex layouts, or integrate formatted dates into rich user interfaces.

Why formatted strings are difficult to customize

When you receive a formatted string like "January 15, 2025", you cannot easily identify where the month ends and the day begins. Different locales place components in different orders. Some locales use different separators. Parsing these strings reliably requires complex logic that duplicates the formatting rules already implemented in the Intl API.

Consider a calendar application that displays dates with the month name in bold. With format(), you would need to:

  1. Detect which characters represent the month name
  2. Account for spaces and punctuation between components
  3. Handle different month formats across locales
  4. Parse the string carefully to avoid breaking the date

This approach is fragile and error-prone. Any change to locale formatting rules breaks your parsing logic.

The formatToParts() method eliminates this problem by providing the components separately. You receive structured data that tells you exactly which part is which, regardless of locale.

Using formatToParts to get date components

The formatToParts() method works identically to format() except for its return value. You create a formatter with the same options, then call formatToParts() instead of format().

const formatter = new Intl.DateTimeFormat("en-US", {
  year: "numeric",
  month: "long",
  day: "numeric"
});

const date = new Date(2025, 0, 15);
const parts = formatter.formatToParts(date);
console.log(parts);

This outputs an array of objects:

[
  { type: "month", value: "January" },
  { type: "literal", value: " " },
  { type: "day", value: "15" },
  { type: "literal", value: ", " },
  { type: "year", value: "2025" }
]

Each object contains a type property identifying what the part represents and a value property containing the actual string. The parts appear in the same order they would in the formatted output.

You can verify this by joining all the values together:

const formatted = parts.map(part => part.value).join("");
console.log(formatted);
// Output: "January 15, 2025"

The concatenated parts produce the exact same output as calling format().

Understanding part types

The type property identifies each component. Different formatting options produce different part types.

For basic date formatting:

const formatter = new Intl.DateTimeFormat("en-US", {
  year: "numeric",
  month: "long",
  day: "numeric"
});

const date = new Date(2025, 0, 15);
const parts = formatter.formatToParts(date);
console.log(parts);
// [
//   { type: "month", value: "January" },
//   { type: "literal", value: " " },
//   { type: "day", value: "15" },
//   { type: "literal", value: ", " },
//   { type: "year", value: "2025" }
// ]

The month type represents the month name or number. The day type represents the day of the month. The year type represents the year. The literal type represents spacing, punctuation, or other text inserted by the formatter.

For dates with weekdays:

const formatter = new Intl.DateTimeFormat("en-US", {
  weekday: "long",
  year: "numeric",
  month: "long",
  day: "numeric"
});

const date = new Date(2025, 0, 15);
const parts = formatter.formatToParts(date);
console.log(parts);
// [
//   { type: "weekday", value: "Wednesday" },
//   { type: "literal", value: ", " },
//   { type: "month", value: "January" },
//   { type: "literal", value: " " },
//   { type: "day", value: "15" },
//   { type: "literal", value: ", " },
//   { type: "year", value: "2025" }
// ]

The weekday type represents the day of the week.

For dates with times:

const formatter = new Intl.DateTimeFormat("en-US", {
  year: "numeric",
  month: "long",
  day: "numeric",
  hour: "numeric",
  minute: "numeric",
  second: "numeric"
});

const date = new Date(2025, 0, 15, 14, 30, 45);
const parts = formatter.formatToParts(date);
console.log(parts);
// [
//   { type: "month", value: "January" },
//   { type: "literal", value: " " },
//   { type: "day", value: "15" },
//   { type: "literal", value: ", " },
//   { type: "year", value: "2025" },
//   { type: "literal", value: " at " },
//   { type: "hour", value: "2" },
//   { type: "literal", value: ":" },
//   { type: "minute", value: "30" },
//   { type: "literal", value: ":" },
//   { type: "second", value: "45" },
//   { type: "literal", value: " " },
//   { type: "dayPeriod", value: "PM" }
// ]

The hour, minute, and second types represent time components. The dayPeriod type represents AM or PM in 12-hour format.

Applying custom styling to date parts

The primary use case for formatToParts() is applying different styles to different components. You can process the parts array to wrap specific types in HTML elements.

Making the month name bold:

const formatter = new Intl.DateTimeFormat("en-US", {
  year: "numeric",
  month: "long",
  day: "numeric"
});

const date = new Date(2025, 0, 15);
const parts = formatter.formatToParts(date);
const html = parts
  .map(part => {
    if (part.type === "month") {
      return `<strong>${part.value}</strong>`;
    }
    return part.value;
  })
  .join("");

console.log(html);
// Output: "<strong>January</strong> 15, 2025"

This approach works for any markup language. You can generate HTML, JSX, or any other format by processing the parts array.

Styling the year differently:

const formatter = new Intl.DateTimeFormat("en-US", {
  year: "numeric",
  month: "long",
  day: "numeric"
});

const date = new Date(2025, 0, 15);
const parts = formatter.formatToParts(date);
const html = parts
  .map(part => {
    if (part.type === "year") {
      return `<span class="text-gray-500">${part.value}</span>`;
    }
    return part.value;
  })
  .join("");

console.log(html);
// Output: "January 15, <span class="text-gray-500">2025</span>"

This pattern is common in calendar displays where different components need different visual emphasis.

Building custom date displays with multiple styles

Complex interfaces often combine multiple styling rules. You can apply different classes or elements to different part types simultaneously.

const formatter = new Intl.DateTimeFormat("en-US", {
  weekday: "long",
  year: "numeric",
  month: "long",
  day: "numeric"
});

const date = new Date(2025, 0, 15);
const parts = formatter.formatToParts(date);

const html = parts
  .map(part => {
    switch (part.type) {
      case "weekday":
        return `<span class="weekday">${part.value}</span>`;
      case "month":
        return `<span class="month">${part.value}</span>`;
      case "day":
        return `<span class="day">${part.value}</span>`;
      case "year":
        return `<span class="year">${part.value}</span>`;
      case "literal":
        return `<span class="literal">${part.value}</span>`;
      default:
        return part.value;
    }
  })
  .join("");

console.log(html);
// Output: "<span class="weekday">Wednesday</span><span class="literal">, </span><span class="month">January</span><span class="literal"> </span><span class="day">15</span><span class="literal">, </span><span class="year">2025</span>"

This granular control enables precise styling for each component. You can then use CSS to style each class differently.

Creating custom date layouts

You can rearrange date components into custom layouts that differ from the standard locale format. Extract specific parts and compose them in any order.

const formatter = new Intl.DateTimeFormat("en-US", {
  year: "numeric",
  month: "long",
  day: "numeric"
});

const date = new Date(2025, 0, 15);
const parts = formatter.formatToParts(date);

const day = parts.find(p => p.type === "day").value;
const month = parts.find(p => p.type === "month").value;
const year = parts.find(p => p.type === "year").value;

const customLayout = `
  <div class="date-card">
    <div class="day-large">${day}</div>
    <div class="month-small">${month}</div>
    <div class="year-small">${year}</div>
  </div>
`;

console.log(customLayout);

This creates a vertical card layout with the day prominently displayed, followed by the month and year. The components remain properly localized even though the layout differs from standard formatting.

All available part types

The type property can have these values depending on the formatting options used:

  • weekday: Day of the week like Monday or Mon
  • era: Era indicator like BC, AD, or BCE
  • year: Year like 2025
  • month: Month name or number like January or 01
  • day: Day of the month like 15
  • dayPeriod: AM or PM or other locale-specific day periods
  • hour: Hour like 14 or 2
  • minute: Minute like 30
  • second: Second like 45
  • fractionalSecond: Milliseconds or other fractional seconds
  • timeZoneName: Time zone name like PST or Pacific Standard Time
  • literal: Spacing, punctuation, or other text added by formatting
  • relatedYear: Gregorian year in alternative calendar systems
  • yearName: Named year in some calendar systems
  • unknown: Unrecognized tokens

Not every formatting option produces every part type. The parts you receive depend on the date value and the formatter configuration.

Dates with era indicators:

const formatter = new Intl.DateTimeFormat("en-US", {
  year: "numeric",
  era: "short"
});

const date = new Date(-100, 0, 1);
const parts = formatter.formatToParts(date);
console.log(parts);
// [
//   { type: "year", value: "101" },
//   { type: "literal", value: " " },
//   { type: "era", value: "BC" }
// ]

Dates with time zones:

const formatter = new Intl.DateTimeFormat("en-US", {
  year: "numeric",
  month: "long",
  day: "numeric",
  hour: "numeric",
  minute: "numeric",
  timeZoneName: "short"
});

const date = new Date(2025, 0, 15, 14, 30);
const parts = formatter.formatToParts(date);
console.log(parts);
// Parts will include { type: "timeZoneName", value: "PST" } or similar

Dates with fractional seconds:

const formatter = new Intl.DateTimeFormat("en-US", {
  hour: "numeric",
  minute: "numeric",
  second: "numeric",
  fractionalSecondDigits: 3
});

const date = new Date(2025, 0, 15, 14, 30, 45, 123);
const parts = formatter.formatToParts(date);
// Parts will include { type: "fractionalSecond", value: "123" }

Highlighting date components conditionally

Some applications highlight specific date components based on business logic. With formatToParts(), you can apply styling based on the date value while maintaining proper formatting.

const formatter = new Intl.DateTimeFormat("en-US", {
  year: "numeric",
  month: "long",
  day: "numeric"
});

function formatDateWithHighlight(date) {
  const parts = formatter.formatToParts(date);
  const isWeekend = date.getDay() === 0 || date.getDay() === 6;

  const html = parts
    .map(part => {
      if (part.type === "day" && isWeekend) {
        return `<span class="text-blue-600 font-bold">${part.value}</span>`;
      }
      return part.value;
    })
    .join("");

  return html;
}

const saturday = new Date(2025, 0, 18);
console.log(formatDateWithHighlight(saturday));
// Output: "January <span class="text-blue-600 font-bold">18</span>, 2025"

const monday = new Date(2025, 0, 13);
console.log(formatDateWithHighlight(monday));
// Output: "January 13, 2025"

The date receives proper formatting for the locale while conditional styling applies based on business logic.

Creating accessible date displays

You can use formatToParts() to add accessibility attributes to formatted dates. This helps screen readers announce values correctly.

const formatter = new Intl.DateTimeFormat("en-US", {
  year: "numeric",
  month: "long",
  day: "numeric"
});

function formatAccessibleDate(date) {
  const parts = formatter.formatToParts(date);
  const formatted = parts.map(part => part.value).join("");
  const isoDate = date.toISOString().split('T')[0];

  return `<time datetime="${isoDate}">${formatted}</time>`;
}

const date = new Date(2025, 0, 15);
console.log(formatAccessibleDate(date));
// Output: "<time datetime="2025-01-15">January 15, 2025</time>"

This ensures screen readers can announce the date properly while the display shows the locale-formatted version.

How parts preserve locale-specific formatting

The parts array maintains locale-specific formatting rules automatically. Different locales place components in different orders and use different formats, but formatToParts() handles these differences.

const usFormatter = new Intl.DateTimeFormat("en-US", {
  year: "numeric",
  month: "long",
  day: "numeric"
});

const date = new Date(2025, 0, 15);
console.log(usFormatter.formatToParts(date));
// [
//   { type: "month", value: "January" },
//   { type: "literal", value: " " },
//   { type: "day", value: "15" },
//   { type: "literal", value: ", " },
//   { type: "year", value: "2025" }
// ]

const ukFormatter = new Intl.DateTimeFormat("en-GB", {
  year: "numeric",
  month: "long",
  day: "numeric"
});

console.log(ukFormatter.formatToParts(date));
// [
//   { type: "day", value: "15" },
//   { type: "literal", value: " " },
//   { type: "month", value: "January" },
//   { type: "literal", value: " " },
//   { type: "year", value: "2025" }
// ]

British formatting places the day before the month. Your styling code processes the parts array the same way regardless of locale, and the formatting adapts automatically.

const jpFormatter = new Intl.DateTimeFormat("ja-JP", {
  year: "numeric",
  month: "long",
  day: "numeric"
});

console.log(jpFormatter.formatToParts(date));
// [
//   { type: "year", value: "2025" },
//   { type: "literal", value: "年" },
//   { type: "month", value: "1月" },
//   { type: "day", value: "15" },
//   { type: "literal", value: "日" }
// ]

Japanese formatting uses different order and includes character literals like 年 (year) and 日 (day). The parts array reflects these locale-specific conventions automatically.

Combining formatToParts with framework components

Modern frameworks like React can use formatToParts() to build components efficiently.

function DateDisplay({ date, locale, options }) {
  const formatter = new Intl.DateTimeFormat(locale, options);
  const parts = formatter.formatToParts(date);

  return (
    <span className="date-display">
      {parts.map((part, index) => {
        if (part.type === "month") {
          return <strong key={index}>{part.value}</strong>;
        }
        if (part.type === "year") {
          return <span key={index} className="text-sm text-gray-500">{part.value}</span>;
        }
        return <span key={index}>{part.value}</span>;
      })}
    </span>
  );
}

This component applies different styling to different parts while maintaining proper formatting for any locale.

When to use formatToParts versus format

Use format() when you need a simple formatted string without any customization. This is the common case for most date display.

Use formatToParts() when you need to:

  • Apply different styling to different parts of the date
  • Build HTML or JSX with formatted dates
  • Add attributes or metadata to specific components
  • Rearrange date components into custom layouts
  • Integrate formatted dates into complex layouts
  • Process formatted output programmatically

The formatToParts() method has slightly more overhead than format() because it creates an array of objects instead of a single string. This difference is negligible for typical applications, but if you format thousands of dates per second, format() performs better.

For most applications, choose based on your styling needs rather than performance concerns. If you do not need to customize the output, use format(). If you need custom styling or markup, use formatToParts().