Metadata API en Next.js: cómo usar metadata estático y generateMetadata dinámico

DUGLAS MORENODUGLAS MORENO 👁 14

Metadata API en Next.js: metadata estático y generateMetadata dinámico

Introducción

Si estás construyendo una aplicación con Next.js y te preocupa el posicionamiento en buscadores o la forma en que tus enlaces se ven al compartirlos en redes sociales, seguramente ya has escuchado hablar de las etiquetas <meta>. Hasta la versión 13 de Next.js, agregar esas etiquetas requería usar el componente Head de next/head o modificar manualmente el archivo _document.js. Con la llegada del App Router y la Metadata API, todo cambió: ahora puedes definir los metadatos de forma declarativa, tanto de manera estática como dinámica, directamente en tus archivos de ruta.

Este artículo está dirigido a desarrolladores que ya conocen los conceptos básicos de Next.js (routing, componentes de servidor y cliente) y quieren profundizar en cómo optimizar el <head> de sus páginas usando la Metadata API. Aprenderás:

  • Qué es la Metadata API y por qué es útil.
  • Cómo definir metadata estático en layout y páginas.
  • Cómo usar la función generateMetadata para crear metadatos dinámicos basado en parámetros, datos externos o estado.
  • Qué salida HTML se genera en cada caso y cómo verificarla.
  • Buenas prácticas y errores comunes que debes evitar.

Al final, tendrás un patrón claro para aplicar metadata en cualquier proyecto Next.js, mejorando tanto el SEO como la experiencia de compartición en redes.


¿Qué es la Metadata API?

La Metadata API es un conjunto de convenciones que permite exportar un objeto llamado metadata o una función async llamada generateMetadata desde un archivo de ruta (page.js, layout.js o route.js). Next.js toma ese objeto o el resultado de la función y lo inserta automáticamente en el <head> del documento HTML renderizado.

Este enfoque tiene varias ventajas:

  • Declarativo: No necesitas importar componentes ni preocuparte por el orden de inserción.
  • Tipado: Si usas TypeScript, Next.js brinda tipos para el objeto metadata, lo que ayuda a evitar errores.
  • Cacheado y streaming: Los metadatos se calculan en el servidor y pueden ser incluidos en el HTML inicial, lo que beneficia al SEO y a los rastreadores de redes sociales.
  • Compatibilidad con el App Router: Funciona tanto en rutas de servidor como en rutas de cliente, aunque lo más común es definirlo en componentes de servidor.

El objeto metadata puede contener campos como title, description, keywords, authors, creator, publisher, openGraph, twitter, icons, manifest, entre otros. Cada campo corresponde a una o más etiquetas <meta> o <link> que Next.js genera.


Metadata estático

El metadata estático es aquel que no depende de ningún parámetro de ruta, de query strings ni de datos externos. Es útil para definir los metadatos de páginas que siempre muestran la misma información, como la página de inicio, una página de acerca de o un documento legal.

Definiendo metadata en un layout

En el App Router, los layouts envuelven a una o más páginas y son ideales para definir metadata que se aplica a todo un segmento de la ruta. Por ejemplo, puedes colocar el metadata del sitio completo en app/layout.js.

// app/layout.js
export const metadata = {
  title: 'Mi Sitio Web',
  description: 'Descripción general del sitio que aparece en los resultados de búsqueda.',
  authors: [{ name: 'Juan Pérez' }],
  openGraph: {
    title: 'Mi Sitio Web',
    description: 'Visita mi sitio para aprender sobre desarrollo web.',
    url: 'https://misitio.example.com',
    siteName: 'Mi Sitio',
    images: [
      {
        url: '/og-image.jpg',
        width: 1200,
        height: 630,
        alt: 'Imagen previa del sitio'
      }
    ]
  },
  twitter: {
    card: 'summary_large_image',
    title: 'Mi Sitio Web',
    description: 'Descripción para Twitter',
    images: ['/og-image.jpg']
  }
};

export default function RootLayout({ children }) {
  return (
    <html lang="es">
      <body>{children}</body>
    </html>
  );
}

Salida esperada en el <head> (simplificada):

<head>
  <title>Mi Sitio Web</title>
  <meta name="description" content="Descripción general del sitio que aparece en los resultados de búsqueda." />
  <meta name="author" content="Juan Pérez" />
  <!-- Open Graph -->
  <meta property="og:title" content="Mi Sitio Web" />
  <meta property="og:description" content="Visita mi sitio para aprender sobre desarrollo web." />
  <meta property="og:url" content="https://misitio.example.com" />
  <meta property="og:site_name" content="Mi Sitio" />
  <meta property="og:image" content="https://misitio.example.com/og-image.jpg" />
  <meta property="og:image:width" content="1200" />
  <meta property="og:image:height" content="630" />
  <meta property="og:image:alt" content="Imagen previa del sitio" />
  <!-- Twitter Card -->
  <meta name="twitter:card" content="summary_large_image" />
  <meta name="twitter:title" content="Mi Sitio Web" />
  <meta name="twitter:description" content="Descripción para Twitter" />
  <meta name="twitter:image" content="https://misitio.example.com/og-image.jpg" />
</head>

Nota: Las URLs absolutas se generan a partir de la base del sitio; si estás en desarrollo con next dev, el host será localhost:3000`.

Metadata estático en una página

Si deseas que una página tenga metadata diferente al layout, simplemente exporta tu propio objeto metadata en el archivo page.js. Next.js combina (merge) el metadata del layout con el de la página, dando prioridad a los valores de la página cuando hay conflicto.

// app/about/page.js
export const metadata = {
  title: 'Acerca de Nosotros',
  description: 'Conoce más sobre nuestro equipo y nuestra misión.',
  authors: [{ name: 'Ana Gómez' }],
};

export default function AboutPage() {
  return <h1>Acerca de Nosotros</h1>;
}

Resultado en <head> (asumiendo que el layout ya tiene su metadata):

<head>
  <title>Acerca de Nosotros</title>
  <meta name="description" content="Conoce más sobre nuestro equipo y nuestra misión." />
  <meta name="author" content="Ana Gómez" />
  <!-- Los metadatos Open Graph y Twitter del layout se heredan -->
</head>

Verificando el metadata

Para ver el metadata generado, inicia la aplicación con next dev y abre el código fuente de la página (clic derecho → Ver fuente de la página). Busca dentro de la etiqueta <head> las etiquetas que describimos. También puedes usar extensiones de navegador como "SEO Meta in 1 Click" para validar Open Graph y Twitter Cards.


Metadata dinámico con generateMetadata

El metadata estático es excelente cuando la información es fija, pero muchas veces necesitamos que los metadatos cambien según la ruta (por ejemplo, el título de un artículo de blog) o según datos obtenidos de una API externa. Para eso sirve la función generateMetadata.

generateMetadata es una función async que recibe dos argumentos: params (los parámetros de ruta dinámica) y searchParams (las query strings). Debes exportarla desde el mismo archivo donde exportas el componente de página o layout. Next.js llama a esta función durante la renderización en el servidor y usa el objeto devuelto para construir el <head>.

Firma de la función

// app/blog/[slug]/page.js
export async function generateMetadata(params, searchParams) {
  // params.slug contiene el valor del segmento dinámico
  // searchParams es un objeto con las query strings
  return {
    title: `Artículo: ${params.slug}`,
    description: `Descripción del artículo ${params.slug}`,
  };
}

export default function BlogPost({ params }) {
  return <h1>Artículo: {params.slug}</h1>;
}

En este ejemplo, el título y la descripción se construyen usando el parámetro slug. Si la URL es /blog/mi-primer-post, el metadata será:

<head>
  <title>Artículo: mi-primer-post</title>
  <meta name="description" content="Descripción del artículo mi-primer-post" />
</head>

Obteniendo datos externos

Supongamos que tienes una API interna que devuelve los datos de un artículo. Puedes hacer una llamada fetch dentro de generateMetadata. Recuerda que esta función se ejecuta en el servidor, por lo que tienes acceso a variables de entorno y a la red (si la configuración de Next.js lo permite).

// app/blog/[slug]/page.js
export async function generateMetadata(params) {
  const res = await fetch(`https://mi-api.example.com/articles/${params.slug}`);
  if (!res.ok) {
    // Devolver un metadata de fallback para evitar errores en el head
    return {
      title: 'Artículo no encontrado',
      description: 'Lo sentimos, el artículo solicitado no existe.'
    };
  }
  const article = await res.json();
  return {
    title: article.title,
    description: article.excerpt,
    openGraph: {
      title: article.title,
      description: article.excerpt,
      url: `https://misitio.example.com/blog/${params.slug}`,
      images: [{ url: article.coverImage }]
    },
    twitter: {
      card: 'summary_large_image',
      title: article.title,
      description: article.excerpt,
      images: [article.coverImage]
    }
  };
}

export default function BlogPost({ params }) {
  // En un caso real, aquí también harías fetch para obtener el contenido
  return (
    <div>
      <h1>Título del artículo</h1>
      {/* ... */}
    </div>
  );
}

Salida esperada (asumiendo que la API responde con):

{
  "title": "Introducción a React Server Components",
  "excerpt": "Aprende cómo los Server Components cambian la forma de construir UI en React.\

Comentarios (0)

Sé el primero en comentar.

Dejá tu comentario