Cómo formatear fechas para diferentes locales en React Router v7

Mostrar fechas en formatos específicos por región

Problema

Las fechas no tienen un formato escrito universal. La secuencia numérica 10/12/2025 significa 10 de octubre en Estados Unidos pero 10 de diciembre en el Reino Unido. Incluso formatos como "Oct 12, 2025" asumen nombres de meses en inglés y un orden específico que puede no coincidir con las expectativas del usuario. Cuando una aplicación muestra fechas en un único formato fijo, resulta extraño para usuarios en regiones que siguen convenciones diferentes, reduciendo la claridad y la confianza.

Diferentes locales tienen reglas distintas para la presentación de fechas, incluyendo el orden del día, mes y año, la elección de separadores, y si los nombres de los meses se escriben completos o abreviados. Ignorar estas convenciones obliga a los usuarios a traducir mentalmente las fechas a su formato familiar, aumentando la carga cognitiva y el riesgo de malinterpretación.

Solución

Formatear los valores de fecha según el locale del usuario delegando la lógica de presentación a las APIs de internacionalización que entienden las convenciones regionales. En lugar de construir manualmente cadenas de fecha, pasa objetos de fecha a funciones o componentes de formateo que aplican el orden correcto, separadores y nombres de meses para el locale activo.

React-intl proporciona tanto componentes declarativos como métodos imperativos que aceptan opciones estándar de formateo de fechas y producen cadenas adaptadas al locale. Al especificar qué partes de la fecha incluir y su nivel de detalle, controlas el estilo de salida mientras la biblioteca maneja la variación regional. Este enfoque mantiene la lógica de formateo de fechas separada de los componentes de visualización y asegura la consistencia en toda la aplicación.

Pasos

1. Formatear fechas en componentes con FormattedDate

Usa el componente FormattedDate de react-intl para renderizar fechas con formato específico del locale. El componente acepta un valor de fecha y opciones de formateo que corresponden a 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>
  );
}

El componente FormattedDate utiliza las APIs formatDate e Intl.DateTimeFormat para producir una cadena que coincide con el locale proporcionado por el IntlProvider circundante. Las opciones year, month y day controlan qué partes aparecen y su formato.

2. Formatear fechas imperativamente con useIntl

Utiliza el hook useIntl para acceder al método formatDate cuando necesites una cadena de fecha formateada para contextos no renderizados como atributos, etiquetas aria o transformaciones de datos.

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>
  );
}

La función formatDate acepta un valor de fecha y opciones Intl.DateTimeFormatOptions opcionales y devuelve una cadena formateada según la configuración regional. Esto es útil cuando la fecha formateada debe incrustarse en otra cadena o utilizarse como valor de una propiedad.

3. Crear un helper reutilizable para formatear fechas

Extrae patrones comunes de formato de fecha en una función helper que envuelve intl.formatDate para garantizar la consistencia en toda la aplicación.

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",
      }),
  };
}

Este hook centraliza la lógica de formateo y facilita la aplicación de estilos de fecha consistentes en toda la aplicación. Los componentes llaman al método apropiado según el nivel de detalle deseado.

4. Formatear fechas en los loaders de rutas

Cuando se necesita formatear fechas durante la carga de datos, accede al locale desde la solicitud y utiliza createIntl para formatear fechas antes de devolver los datos del cargador.

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>
  );
}

La función createIntl crea un objeto intl que puede formatear fechas fuera de los componentes de React. Este enfoque pre-formatea las fechas en el servidor o durante la generación estática, reduciendo el trabajo del lado del cliente.