How to format dates for different locales in React Router v7
Display dates in region-specific formats
Problem
Dates do not have a universal written format. The numeric sequence 10/12/2025 means October 12 in the United States but December 10 in the United Kingdom. Even formats like "Oct 12, 2025" assume English month names and a specific ordering convention that may not match user expectations. When an application displays dates in a single fixed format, it feels foreign to users in regions that follow different conventions, reducing clarity and trust.
Different locales have distinct rules for date presentation, including the order of day, month, and year, the choice of separators, and whether month names are written in full or abbreviated. Ignoring these conventions forces users to mentally translate dates into their familiar format, increasing cognitive load and the risk of misinterpretation.
Solution
Format date values according to the user's locale by delegating presentation logic to internationalization APIs that understand regional conventions. Instead of manually constructing date strings, pass date objects to formatting functions or components that apply the correct order, separators, and month names for the active locale.
React-intl provides both declarative components and imperative methods that accept standard date formatting options and produce locale-aware strings. By specifying which date parts to include and their level of detail, you control the output style while the library handles regional variation. This approach keeps date formatting logic separate from display components and ensures consistency across the application.
Steps
1. Format dates in components with FormattedDate
Use the FormattedDate component from react-intl to render dates with locale-specific formatting. The component accepts a date value and formatting options that correspond to Intl.DateTimeFormatOptions.
import { FormattedDate } from "react-intl";
export default function EventCard({ event }) {
return (
<article>
<h2>{event.title}</h2>
<time>
<FormattedDate
value={event.date}
year="numeric"
month="long"
day="numeric"
/>
</time>
<p>{event.description}</p>
</article>
);
}
The FormattedDate component uses the formatDate and Intl.DateTimeFormat APIs to produce a string that matches the locale provided by the surrounding IntlProvider. The year, month, and day options control which parts appear and their format.
2. Format dates imperatively with useIntl
Use the useIntl hook to access the formatDate method when you need a formatted date string for non-rendered contexts such as attributes, aria labels, or data transformations.
import { useIntl } from "react-intl";
export default function EventList({ events }) {
const intl = useIntl();
return (
<ul>
{events.map((event) => {
const formattedDate = intl.formatDate(event.date, {
year: "numeric",
month: "short",
day: "numeric",
});
return (
<li key={event.id}>
<a
href={`/events/${event.id}`}
aria-label={`${event.title} on ${formattedDate}`}
>
{event.title}
</a>
</li>
);
})}
</ul>
);
}
The formatDate function accepts a date value and optional Intl.DateTimeFormatOptions and returns a locale-formatted string. This is useful when the formatted date must be embedded in another string or used as a prop value.
3. Create a reusable date formatter helper
Extract common date formatting patterns into a helper function that wraps intl.formatDate to ensure consistency across the application.
import { useIntl } from "react-intl";
export function useDateFormatter() {
const intl = useIntl();
return {
formatShortDate: (date: Date | number) =>
intl.formatDate(date, {
year: "numeric",
month: "numeric",
day: "numeric",
}),
formatLongDate: (date: Date | number) =>
intl.formatDate(date, {
year: "numeric",
month: "long",
day: "numeric",
}),
formatDateTime: (date: Date | number) =>
intl.formatDate(date, {
year: "numeric",
month: "short",
day: "numeric",
hour: "numeric",
minute: "2-digit",
}),
};
}
This hook centralizes formatting logic and makes it easy to apply consistent date styles throughout the application. Components call the appropriate method based on the desired level of detail.
4. Format dates in route loaders
When date formatting is needed during data loading, access the locale from the request and use createIntl to format dates before returning loader data.
import type { Route } from "./+types/event";
import { createIntl, createIntlCache } from "react-intl";
const cache = createIntlCache();
export async function loader({ request }: Route.LoaderArgs) {
const url = new URL(request.url);
const locale = url.searchParams.get("locale") || "en";
const event = await fetchEvent();
const intl = createIntl({ locale, messages: {} }, cache);
return {
event,
formattedDate: intl.formatDate(event.date, {
year: "numeric",
month: "long",
day: "numeric",
}),
};
}
export default function Event({ loaderData }: Route.ComponentProps) {
return (
<article>
<h1>{loaderData.event.title}</h1>
<time>{loaderData.formattedDate}</time>
</article>
);
}
The createIntl function creates an intl object that can format dates outside of React components. This approach pre-formats dates on the server or during static generation, reducing client-side work.