generateMetadata

Puedes usar el objeto metadata o la función generateMetadata para definir metadatos.

El objeto metadata

Para definir metadatos estáticos, exporta un objeto Metadata desde un archivo layout.js o page.js.

import type { Metadata } from 'next'

export const metadata: Metadata = {
  title: '...',
  description: '...',
}

export default function Page() {}
export const metadata = {
  title: '...',
  description: '...',
}

export default function Page() {}

Consulta los Campos de Metadatos para una lista completa de opciones soportadas.

Función generateMetadata

Los metadatos dinámicos que dependen de información dinámica, como parámetros de ruta actuales, datos externos o metadata en segmentos padres, pueden configurarse exportando una función generateMetadata que devuelve un objeto Metadata.

import type { Metadata, ResolvingMetadata } from 'next'

type Props = {
  params: Promise<{ id: string }>
  searchParams: Promise<{ [key: string]: string | string[] | undefined }>
}

export async function generateMetadata(
  { params, searchParams }: Props,
  parent: ResolvingMetadata
): Promise<Metadata> {
  // leer parámetros de ruta
  const { id } = await params

  // obtener datos
  const product = await fetch(`https://.../${id}`).then((res) => res.json())

  // opcionalmente acceder y extender (en lugar de reemplazar) metadatos padres
  const previousImages = (await parent).openGraph?.images || []

  return {
    title: product.title,
    openGraph: {
      images: ['/some-specific-page-image.jpg', ...previousImages],
    },
  }
}

export default function Page({ params, searchParams }: Props) {}
export async function generateMetadata({ params, searchParams }, parent) {
  // leer parámetros de ruta
  const { id } = await params

  // obtener datos
  const product = await fetch(`https://.../${id}`).then((res) => res.json())

  // opcionalmente acceder y extender (en lugar de reemplazar) metadatos padres
  const previousImages = (await parent).openGraph?.images || []

  return {
    title: product.title,
    openGraph: {
      images: ['/some-specific-page-image.jpg', ...previousImages],
    },
  }
}

export default function Page({ params, searchParams }) {}

Es bueno saber:

  • Los metadatos pueden agregarse a archivos layout.js y page.js.
  • Next.js resolverá automáticamente los metadatos y creará las etiquetas <head> relevantes para la página.
  • Las exportaciones del objeto metadata y la función generateMetadata solo son compatibles en Componentes de Servidor.
  • No puedes exportar tanto el objeto metadata como la función generateMetadata desde el mismo segmento de ruta.
  • Las solicitudes fetch dentro de generateMetadata se memorizan automáticamente para los mismos datos entre generateMetadata, generateStaticParams, Layouts, Pages y Server Components.
  • Se puede usar cache de React si fetch no está disponible.
  • Los metadatos basados en archivos tienen mayor prioridad y anularán el objeto metadata y la función generateMetadata.

Referencia

Parámetros

La función generateMetadata acepta los siguientes parámetros:

  • props - Un objeto que contiene los parámetros de la ruta actual:

    • params - Un objeto que contiene los parámetros de ruta dinámica desde el segmento raíz hasta el segmento desde donde se llama a generateMetadata. Ejemplos:

      RutaURLparams
      app/shop/[slug]/page.js/shop/1{ slug: '1' }
      app/shop/[tag]/[item]/page.js/shop/1/2{ tag: '1', item: '2' }
      app/shop/[...slug]/page.js/shop/1/2{ slug: ['1', '2'] }
    • searchParams - Un objeto que contiene los parámetros de búsqueda de la URL actual. Ejemplos:

      URLsearchParams
      /shop?a=1{ a: '1' }
      /shop?a=1&b=2{ a: '1', b: '2' }
      /shop?a=1&a=2{ a: ['1', '2'] }
  • parent - Una promesa de los metadatos resueltos de los segmentos de ruta padres.

Devuelve

generateMetadata debe devolver un objeto Metadata que contenga uno o más campos de metadatos.

Es bueno saber:

  • Si los metadatos no dependen de información en tiempo de ejecución, deben definirse usando el objeto metadata estático en lugar de generateMetadata.
  • Las solicitudes fetch se memorizan automáticamente para los mismos datos entre generateMetadata, generateStaticParams, Layouts, Pages y Server Components. Se puede usar cache de React si fetch no está disponible.
  • searchParams solo está disponible en segmentos page.js.
  • Los métodos redirect() y notFound() de Next.js también pueden usarse dentro de generateMetadata.

Campos de Metadatos

Se soportan los siguientes campos:

title

El atributo title se usa para establecer el título del documento. Puede definirse como una cadena simple o un objeto plantilla opcional.

Cadena
layout.js | page.js
export const metadata = {
  title: 'Next.js',
}
<head> output
<title>Next.js</title>
default

title.default puede usarse para proporcionar un título de respaldo a segmentos de ruta hijos que no definen un title.

app/layout.tsx
import type { Metadata } from 'next'

export const metadata: Metadata = {
  title: {
    default: 'Acme',
  },
}
app/about/page.tsx
import type { Metadata } from 'next'

export const metadata: Metadata = {}

// Salida: <title>Acme</title>
template

title.template puede usarse para agregar un prefijo o sufijo a los titles definidos en segmentos de ruta hijos.

import type { Metadata } from 'next'

export const metadata: Metadata = {
  title: {
    template: '%s | Acme',
    default: 'Acme', // se requiere un valor por defecto al crear una plantilla
  },
}
export const metadata = {
  title: {
    template: '%s | Acme',
    default: 'Acme', // se requiere un valor por defecto al crear una plantilla
  },
}
import type { Metadata } from 'next'

export const metadata: Metadata = {
  title: 'About',
}

// Salida: <title>About | Acme</title>
export const metadata = {
  title: 'About',
}

// Salida: <title>About | Acme</title>

Es bueno saber:

  • title.template aplica a segmentos de ruta hijos y no al segmento donde se define. Esto significa:

    • title.default es requerido cuando agregas un title.template.
    • title.template definido en layout.js no aplicará a un title definido en un page.js del mismo segmento de ruta.
    • title.template definido en page.js no tiene efecto porque una página siempre es el segmento terminal (no tiene segmentos de ruta hijos).
  • title.template no tiene efecto si una ruta no ha definido un title o title.default.

absolute

title.absolute puede usarse para proporcionar un título que ignore title.template establecido en segmentos padres.

import type { Metadata } from 'next'

export const metadata: Metadata = {
  title: {
    template: '%s | Acme',
  },
}
export const metadata = {
  title: {
    template: '%s | Acme',
  },
}
import type { Metadata } from 'next'

export const metadata: Metadata = {
  title: {
    absolute: 'About',
  },
}

// Salida: <title>About</title>
export const metadata = {
  title: {
    absolute: 'About',
  },
}

// Salida: <title>About</title>

Es bueno saber:

  • layout.js

    • title (cadena) y title.default definen el título predeterminado para segmentos hijos (que no definen su propio title). Aumentará title.template del segmento padre más cercano si existe.
    • title.absolute define el título predeterminado para segmentos hijos. Ignora title.template de segmentos padres.
    • title.template define una nueva plantilla de título para segmentos hijos.
  • page.js

    • Si una página no define su propio título, se usará el título resuelto del padre más cercano.
    • title (cadena) define el título de la ruta. Aumentará title.template del segmento padre más cercano si existe.
    • title.absolute define el título de la ruta. Ignora title.template de segmentos padres.
    • title.template no tiene efecto en page.js porque una página siempre es el segmento terminal de una ruta.

description

layout.js | page.js
export const metadata = {
  description: 'El Framework de React para la Web',
}
<head> output
<meta name="description" content="El Framework de React para la Web" />

Otros campos

layout.js | page.js
export const metadata = {
  generator: 'Next.js',
  applicationName: 'Next.js',
  referrer: 'origin-when-cross-origin',
  keywords: ['Next.js', 'React', 'JavaScript'],
  authors: [{ name: 'Seb' }, { name: 'Josh', url: 'https://nextjs.org' }],
  creator: 'Jiachi Liu',
  publisher: 'Sebastian Markbåge',
  formatDetection: {
    email: false,
    address: false,
    telephone: false,
  },
}
<head> output
<meta name="application-name" content="Next.js" />
<meta name="author" content="Seb" />
<link rel="author" href="https://nextjs.org" />
<meta name="author" content="Josh" />
<meta name="generator" content="Next.js" />
<meta name="keywords" content="Next.js,React,JavaScript" />
<meta name="referrer" content="origin-when-cross-origin" />
<meta name="color-scheme" content="dark" />
<meta name="creator" content="Jiachi Liu" />
<meta name="publisher" content="Sebastian Markbåge" />
<meta name="format-detection" content="telephone=no, address=no, email=no" />

metadataBase

metadataBase es una opción conveniente para establecer un prefijo de URL base para campos de metadata que requieren una URL completa.

  • metadataBase permite que los campos de metadata basados en URL definidos en el segmento de ruta actual y siguientes usen una ruta relativa en lugar de una URL absoluta que de otro modo sería requerida.
  • La ruta relativa del campo se compondrá con metadataBase para formar una URL completa.
layout.js | page.js
export const metadata = {
  metadataBase: new URL('https://acme.com'),
  alternates: {
    canonical: '/',
    languages: {
      'en-US': '/en-US',
      'de-DE': '/de-DE',
    },
  },
  openGraph: {
    images: '/og-image.png',
  },
}
<head> output
<link rel="canonical" href="https://acme.com" />
<link rel="alternate" hreflang="en-US" href="https://acme.com/en-US" />
<link rel="alternate" hreflang="de-DE" href="https://acme.com/de-DE" />
<meta property="og:image" content="https://acme.com/og-image.png" />

Es bueno saber:

  • metadataBase normalmente se establece en el archivo raíz app/layout.js para aplicar a campos de metadata basados en URL en todas las rutas.
  • Todos los campos de metadata basados en URL que requieren URLs absolutas pueden configurarse con una opción metadataBase.
  • metadataBase puede contener un subdominio, p.ej. https://app.acme.com o una ruta base, p.ej. https://acme.com/start/from/here
  • Si un campo de metadata proporciona una URL absoluta, metadataBase será ignorado.
  • Usar una ruta relativa en un campo de metadata basado en URL sin configurar un metadataBase causará un error de compilación.
  • Next.js normalizará barras duplicadas entre metadataBase (p.ej. https://acme.com/) y un campo relativo (p.ej. /path) a una sola barra (p.ej. https://acme.com/path)

Composición de URL

La composición de URL favorece la intención del desarrollador sobre la semántica predeterminada de recorrido de directorios.

  • Las barras finales entre metadataBase y campos de metadata se normalizan.
  • Una ruta "absoluta" en un campo de metadata (que normalmente reemplazaría toda la ruta de URL) se trata como una ruta "relativa" (comenzando desde el final de metadataBase).

Por ejemplo, dado el siguiente metadataBase:

import type { Metadata } from 'next'

export const metadata: Metadata = {
  metadataBase: new URL('https://acme.com'),
}
export const metadata = {
  metadataBase: new URL('https://acme.com'),
}

Cualquier campo de metadata que herede el metadataBase anterior y establezca su propio valor se resolverá de la siguiente manera:

Campo de metadataURL resuelta
/https://acme.com
./https://acme.com
paymentshttps://acme.com/payments
/paymentshttps://acme.com/payments
./paymentshttps://acme.com/payments
../paymentshttps://acme.com/payments
https://beta.acme.com/paymentshttps://beta.acme.com/payments

openGraph

layout.js | page.js
export const metadata = {
  openGraph: {
    title: 'Next.js',
    description: 'El Framework React para la Web',
    url: 'https://nextjs.org',
    siteName: 'Next.js',
    images: [
      {
        url: 'https://nextjs.org/og.png', // Debe ser una URL absoluta
        width: 800,
        height: 600,
      },
      {
        url: 'https://nextjs.org/og-alt.png', // Debe ser una URL absoluta
        width: 1800,
        height: 1600,
        alt: 'Mi texto alternativo personalizado',
      },
    ],
    videos: [
      {
        url: 'https://nextjs.org/video.mp4', // Debe ser una URL absoluta
        width: 800,
        height: 600,
      },
    ],
    audio: [
      {
        url: 'https://nextjs.org/audio.mp3', // Debe ser una URL absoluta
      },
    ],
    locale: 'en_US',
    type: 'website',
  },
}
<head> output
<meta property="og:title" content="Next.js" />
<meta property="og:description" content="El Framework React para la Web" />
<meta property="og:url" content="https://nextjs.org/" />
<meta property="og:site_name" content="Next.js" />
<meta property="og:locale" content="en_US" />
<meta property="og:image" content="https://nextjs.org/og.png" />
<meta property="og:image:width" content="800" />
<meta property="og:image:height" content="600" />
<meta property="og:image" content="https://nextjs.org/og-alt.png" />
<meta property="og:image:width" content="1800" />
<meta property="og:image:height" content="1600" />
<meta property="og:image:alt" content="Mi texto alternativo personalizado" />
<meta property="og:video" content="https://nextjs.org/video.mp4" />
<meta property="og:video:width" content="800" />
<meta property="og:video:height" content="600" />
<meta property="og:audio" content="https://nextjs.org/audio.mp3" />
<meta property="og:type" content="website" />
layout.js | page.js
export const metadata = {
  openGraph: {
    title: 'Next.js',
    description: 'El Framework React para la Web',
    type: 'article',
    publishedTime: '2023-01-01T00:00:00.000Z',
    authors: ['Seb', 'Josh'],
  },
}
<head> output
<meta property="og:title" content="Next.js" />
<meta property="og:description" content="El Framework React para la Web" />
<meta property="og:type" content="article" />
<meta property="article:published_time" content="2023-01-01T00:00:00.000Z" />
<meta property="article:author" content="Seb" />
<meta property="article:author" content="Josh" />

Nota importante:

  • Puede ser más conveniente usar la API de Metadatos basada en archivos para imágenes de Open Graph. En lugar de tener que sincronizar la exportación de configuración con archivos reales, la API basada en archivos generará automáticamente los metadatos correctos.

robots

layout.tsx | page.tsx
import type { Metadata } from 'next'

export const metadata: Metadata = {
  robots: {
    index: true,
    follow: true,
    nocache: false,
    googleBot: {
      index: true,
      follow: true,
      noimageindex: false,
      'max-video-preview': -1,
      'max-image-preview': 'large',
      'max-snippet': -1,
    },
  },
}
<head> output
<meta name="robots" content="index, follow" />
<meta
  name="googlebot"
  content="index, follow, max-video-preview:-1, max-image-preview:large, max-snippet:-1"
/>

icons

Nota importante: Recomendamos usar la API de Metadatos basada en archivos para iconos cuando sea posible. En lugar de tener que sincronizar la exportación de configuración con archivos reales, la API basada en archivos generará automáticamente los metadatos correctos.

layout.js | page.js
export const metadata = {
  icons: {
    icon: '/icon.png',
    shortcut: '/shortcut-icon.png',
    apple: '/apple-icon.png',
    other: {
      rel: 'apple-touch-icon-precomposed',
      url: '/apple-touch-icon-precomposed.png',
    },
  },
}
<head> output
<link rel="shortcut icon" href="/shortcut-icon.png" />
<link rel="icon" href="/icon.png" />
<link rel="apple-touch-icon" href="/apple-icon.png" />
<link
  rel="apple-touch-icon-precomposed"
  href="/apple-touch-icon-precomposed.png"
/>
layout.js | page.js
export const metadata = {
  icons: {
    icon: [
      { url: '/icon.png' },
      new URL('/icon.png', 'https://example.com'),
      { url: '/icon-dark.png', media: '(prefers-color-scheme: dark)' },
    ],
    shortcut: ['/shortcut-icon.png'],
    apple: [
      { url: '/apple-icon.png' },
      { url: '/apple-icon-x3.png', sizes: '180x180', type: 'image/png' },
    ],
    other: [
      {
        rel: 'apple-touch-icon-precomposed',
        url: '/apple-touch-icon-precomposed.png',
      },
    ],
  },
}
<head> output
<link rel="shortcut icon" href="/shortcut-icon.png" />
<link rel="icon" href="/icon.png" />
<link rel="icon" href="https://example.com/icon.png" />
<link rel="icon" href="/icon-dark.png" media="(prefers-color-scheme: dark)" />
<link rel="apple-touch-icon" href="/apple-icon.png" />
<link
  rel="apple-touch-icon-precomposed"
  href="/apple-touch-icon-precomposed.png"
/>
<link
  rel="apple-touch-icon"
  href="/apple-icon-x3.png"
  sizes="180x180"
  type="image/png"
/>

Nota importante: Las etiquetas meta msapplication-* ya no son compatibles en las versiones Chromium de Microsoft Edge, por lo que ya no son necesarias.

themeColor

Obsoleto: La opción themeColor en metadata está obsoleta a partir de Next.js 14. Utilice la configuración viewport en su lugar.

colorScheme

Obsoleto: La opción colorScheme en metadata está obsoleta a partir de Next.js 14. Utilice la configuración viewport en su lugar.

manifest

Un manifiesto de aplicación web, como se define en la especificación de Manifiesto de Aplicación Web.

layout.js | page.js
export const metadata = {
  manifest: 'https://nextjs.org/manifest.json',
}
<head> output
<link rel="manifest" href="https://nextjs.org/manifest.json" />

twitter

La especificación de Twitter se usa (sorprendentemente) para más que solo X (anteriormente conocido como Twitter).

Más información sobre la referencia de marcado de Twitter Cards.

layout.js | page.js
export const metadata = {
  twitter: {
    card: 'summary_large_image',
    title: 'Next.js',
    description: 'El Framework React para la Web',
    siteId: '1467726470533754880',
    creator: '@nextjs',
    creatorId: '1467726470533754880',
    images: ['https://nextjs.org/og.png'], // Debe ser una URL absoluta
  },
}
<head> output
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site:id" content="1467726470533754880" />
<meta name="twitter:creator" content="@nextjs" />
<meta name="twitter:creator:id" content="1467726470533754880" />
<meta name="twitter:title" content="Next.js" />
<meta name="twitter:description" content="El Framework React para la Web" />
<meta name="twitter:image" content="https://nextjs.org/og.png" />
layout.js | page.js
export const metadata = {
  twitter: {
    card: 'app',
    title: 'Next.js',
    description: 'El Framework React para la Web',
    siteId: '1467726470533754880',
    creator: '@nextjs',
    creatorId: '1467726470533754880',
    images: {
      url: 'https://nextjs.org/og.png',
      alt: 'Logo de Next.js',
    },
    app: {
      name: 'twitter_app',
      id: {
        iphone: 'twitter_app://iphone',
        ipad: 'twitter_app://ipad',
        googleplay: 'twitter_app://googleplay',
      },
      url: {
        iphone: 'https://iphone_url',
        ipad: 'https://ipad_url',
      },
    },
  },
}
<head> output
<meta name="twitter:site:id" content="1467726470533754880" />
<meta name="twitter:creator" content="@nextjs" />
<meta name="twitter:creator:id" content="1467726470533754880" />
<meta name="twitter:title" content="Next.js" />
<meta name="twitter:description" content="El Framework React para la Web" />
<meta name="twitter:card" content="app" />
<meta name="twitter:image" content="https://nextjs.org/og.png" />
<meta name="twitter:image:alt" content="Logo de Next.js" />
<meta name="twitter:app:name:iphone" content="twitter_app" />
<meta name="twitter:app:id:iphone" content="twitter_app://iphone" />
<meta name="twitter:app:id:ipad" content="twitter_app://ipad" />
<meta name="twitter:app:id:googleplay" content="twitter_app://googleplay" />
<meta name="twitter:app:url:iphone" content="https://iphone_url" />
<meta name="twitter:app:url:ipad" content="https://ipad_url" />
<meta name="twitter:app:name:ipad" content="twitter_app" />
<meta name="twitter:app:name:googleplay" content="twitter_app" />

viewport

Obsoleto: La opción viewport en metadata está obsoleta a partir de Next.js 14. Utilice la configuración viewport en su lugar.

verification

layout.js | page.js
export const metadata = {
  verification: {
    google: 'google',
    yandex: 'yandex',
    yahoo: 'yahoo',
    other: {
      me: ['my-email', 'my-link'],
    },
  },
}
<head> output
<meta name="google-site-verification" content="google" />
<meta name="y_key" content="yahoo" />
<meta name="yandex-verification" content="yandex" />
<meta name="me" content="my-email" />
<meta name="me" content="my-link" />

appleWebApp

layout.js | page.js
export const metadata = {
  itunes: {
    appId: 'myAppStoreID',
    appArgument: 'myAppArgument',
  },
  appleWebApp: {
    title: 'Apple Web App',
    statusBarStyle: 'black-translucent',
    startupImage: [
      '/assets/startup/apple-touch-startup-image-768x1004.png',
      {
        url: '/assets/startup/apple-touch-startup-image-1536x2008.png',
        media: '(device-width: 768px) and (device-height: 1024px)',
      },
    ],
  },
}
<head> output
<meta
  name="apple-itunes-app"
  content="app-id=myAppStoreID, app-argument=myAppArgument"
/>
<meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-title" content="Apple Web App" />
<link
  href="/assets/startup/apple-touch-startup-image-768x1004.png"
  rel="apple-touch-startup-image"
/>
<link
  href="/assets/startup/apple-touch-startup-image-1536x2008.png"
  media="(device-width: 768px) and (device-height: 1024px)"
  rel="apple-touch-startup-image"
/>
<meta
  name="apple-mobile-web-app-status-bar-style"
  content="black-translucent"
/>

alternates

layout.js | page.js
export const metadata = {
  alternates: {
    canonical: 'https://nextjs.org',
    languages: {
      'en-US': 'https://nextjs.org/en-US',
      'de-DE': 'https://nextjs.org/de-DE',
    },
    media: {
      'only screen and (max-width: 600px)': 'https://nextjs.org/mobile',
    },
    types: {
      'application/rss+xml': 'https://nextjs.org/rss',
    },
  },
}
<head> output
<link rel="canonical" href="https://nextjs.org" />
<link rel="alternate" hreflang="en-US" href="https://nextjs.org/en-US" />
<link rel="alternate" hreflang="de-DE" href="https://nextjs.org/de-DE" />
<link
  rel="alternate"
  media="only screen and (max-width: 600px)"
  href="https://nextjs.org/mobile"
/>
<link
  rel="alternate"
  type="application/rss+xml"
  href="https://nextjs.org/rss"
/>
layout.js | page.js
export const metadata = {
  appLinks: {
    ios: {
      url: 'https://nextjs.org/ios',
      app_store_id: 'app_store_id',
    },
    android: {
      package: 'com.example.android/package',
      app_name: 'app_name_android',
    },
    web: {
      url: 'https://nextjs.org/web',
      should_fallback: true,
    },
  },
}
<head> output
<meta property="al:ios:url" content="https://nextjs.org/ios" />
<meta property="al:ios:app_store_id" content="app_store_id" />
<meta property="al:android:package" content="com.example.android/package" />
<meta property="al:android:app_name" content="app_name_android" />
<meta property="al:web:url" content="https://nextjs.org/web" />
<meta property="al:web:should_fallback" content="true" />

archives

Describe una colección de registros, documentos u otros materiales de interés histórico (fuente).

layout.js | page.js
export const metadata = {
  archives: ['https://nextjs.org/13'],
}
<head> output
<link rel="archives" href="https://nextjs.org/13" />

assets

layout.js | page.js
export const metadata = {
  assets: ['https://nextjs.org/assets'],
}
<head> output
<link rel="assets" href="https://nextjs.org/assets" />

bookmarks

layout.js | page.js
export const metadata = {
  bookmarks: ['https://nextjs.org/13'],
}
<head> output
<link rel="bookmarks" href="https://nextjs.org/13" />

category

layout.js | page.js
export const metadata = {
  category: 'technology',
}
<head> output
<meta name="category" content="technology" />

facebook

Puedes conectar una aplicación de Facebook o una cuenta de Facebook a tu página web para ciertos Plugins Sociales de Facebook Documentación de Facebook

Nota importante: Puedes especificar appId o admins, pero no ambos.

layout.js | page.js
export const metadata = {
  facebook: {
    appId: '12345678',
  },
}
<head> output
<meta property="fb:app_id" content="12345678" />
layout.js | page.js
export const metadata = {
  facebook: {
    admins: '12345678',
  },
}
<head> output
<meta property="fb:admins" content="12345678" />

Si deseas generar múltiples etiquetas meta fb:admins puedes usar un valor de array.

layout.js | page.js
export const metadata = {
  facebook: {
    admins: ['12345678', '87654321'],
  },
}
<head> output
<meta property="fb:admins" content="12345678" />
<meta property="fb:admins" content="87654321" />

pinterest

Puedes habilitar o deshabilitar Pinterest Rich Pins en tu página web.

layout.js | page.js
export const metadata = {
  pinterest: {
    richPin: true,
  },
}
<head> output
<meta name="pinterest-rich-pin" content="true" />

other

Todas las opciones de metadatos deberían estar cubiertas usando el soporte incorporado. Sin embargo, puede haber etiquetas meta personalizadas específicas para tu sitio o nuevas etiquetas meta recién lanzadas. Puedes usar la opción other para renderizar cualquier etiqueta meta personalizada.

layout.js | page.js
export const metadata = {
  other: {
    custom: 'meta',
  },
}
<head> output
<meta name="custom" content="meta" />

Si deseas generar múltiples etiquetas meta con la misma clave puedes usar un valor de array.

layout.js | page.js
export const metadata = {
  other: {
    custom: ['meta1', 'meta2'],
  },
}
<head> output
<meta name="custom" content="meta1" /> <meta name="custom" content="meta2" />

Metadatos no compatibles

Los siguientes tipos de metadatos no tienen soporte incorporado actualmente. Sin embargo, aún pueden renderizarse en el layout o página directamente.

Tipos

Puede agregar seguridad de tipos a sus metadatos utilizando el tipo Metadata. Si está utilizando el plugin integrado de TypeScript en su IDE, no necesita agregar el tipo manualmente, pero aún puede hacerlo explícitamente si lo desea.

Objeto metadata

layout.tsx | page.tsx
import type { Metadata } from 'next'

export const metadata: Metadata = {
  title: 'Next.js',
}

Función generateMetadata

Función regular
layout.tsx | page.tsx
import type { Metadata } from 'next'

export function generateMetadata(): Metadata {
  return {
    title: 'Next.js',
  }
}
Función asíncrona
layout.tsx | page.tsx
import type { Metadata } from 'next'

export async function generateMetadata(): Promise<Metadata> {
  return {
    title: 'Next.js',
  }
}
Con props de segmento
layout.tsx | page.tsx
import type { Metadata } from 'next'

type Props = {
  params: Promise<{ id: string }>
  searchParams: Promise<{ [key: string]: string | string[] | undefined }>
}

export function generateMetadata({ params, searchParams }: Props): Metadata {
  return {
    title: 'Next.js',
  }
}

export default function Page({ params, searchParams }: Props) {}
Con metadatos padre
layout.tsx | page.tsx
import type { Metadata, ResolvingMetadata } from 'next'

export async function generateMetadata(
  { params, searchParams }: Props,
  parent: ResolvingMetadata
): Promise<Metadata> {
  return {
    title: 'Next.js',
  }
}
Proyectos JavaScript

Para proyectos JavaScript, puede usar JSDoc para agregar seguridad de tipos.

layout.js | page.js
/** @type {import("next").Metadata} */
export const metadata = {
  title: 'Next.js',
}
MetadatosRecomendación
<meta http-equiv="...">Use encabezados HTTP apropiados mediante redirect(), Middleware, Security Headers
<base>Renderice la etiqueta en el layout o la página misma.
<noscript>Renderice la etiqueta en el layout o la página misma.
<style>Más información sobre estilos en Next.js.
<script>Más información sobre uso de scripts.
<link rel="stylesheet" />Importe hojas de estilo directamente en el layout o la página misma.
<link rel="preload />Use método preload de ReactDOM
<link rel="preconnect" />Use método preconnect de ReactDOM
<link rel="dns-prefetch" />Use método prefetchDNS de ReactDOM

Pistas de recursos

El elemento <link> tiene varias palabras clave rel que pueden usarse para indicar al navegador que es probable que se necesite un recurso externo. El navegador usa esta información para aplicar optimizaciones de precarga según la palabra clave.

Aunque la API de Metadata no admite directamente estas pistas, puede usar nuevos métodos de ReactDOM para insertarlas de forma segura en el <head> del documento.

'use client'

import ReactDOM from 'react-dom'

export function PreloadResources() {
  ReactDOM.preload('...', { as: '...' })
  ReactDOM.preconnect('...', { crossOrigin: '...' })
  ReactDOM.prefetchDNS('...')

  return '...'
}
'use client'

import ReactDOM from 'react-dom'

export function PreloadResources() {
  ReactDOM.preload('...', { as: '...' })
  ReactDOM.preconnect('...', { crossOrigin: '...' })
  ReactDOM.prefetchDNS('...')

  return '...'
}

Inicia la carga de un recurso temprano en el ciclo de vida de renderizado de la página (navegador). Documentación MDN.

ReactDOM.preload(href: string, options: { as: string })
<head> output
<link rel="preload" href="..." as="..." />

Inicia preemptivamente una conexión a un origen. Documentación MDN.

ReactDOM.preconnect(href: string, options?: { crossOrigin?: string })
<head> output
<link rel="preconnect" href="..." crossorigin />

Intenta resolver un nombre de dominio antes de que se soliciten los recursos. Documentación MDN.

ReactDOM.prefetchDNS(href: string)
<head> output
<link rel="dns-prefetch" href="..." />

Nota importante:

  • Estos métodos actualmente solo son compatibles en Componentes de Cliente, que aún se renderizan en el servidor en la carga inicial de la página.
  • Las funciones integradas de Next.js como next/font, next/image y next/script manejan automáticamente las pistas de recursos relevantes.

Comportamiento

Campos predeterminados

Hay dos etiquetas meta predeterminadas que siempre se agregan, incluso si una ruta no define metadatos:

<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />

Nota importante: Puede sobrescribir la etiqueta meta viewport predeterminada.

Transmisión (streaming) de metadatos

Los metadatos devueltos por generateMetadata se transmiten al cliente. Esto permite a Next.js inyectar metadatos en el HTML tan pronto como se resuelven.

Dado que los metadatos de página están dirigidos principalmente a bots y rastreadores, Next.js transmitirá metadatos para bots que pueden ejecutar JavaScript e inspeccionar el DOM completo de la página (por ejemplo, Googlebot). Sin embargo, los metadatos seguirán bloqueando el renderizado de la página para bots limitados a HTML (por ejemplo, Twitterbot) ya que estos no pueden ejecutar JavaScript mientras rastrean.

Next.js detecta automáticamente el agente de usuario de las solicitudes entrantes para determinar si servir metadatos transmitidos o recurrir a metadatos bloqueantes.

Si necesita personalizar esta lista, puede definirla manualmente usando la opción htmlLimitedBots en next.config.js. Next.js asegurará que los agentes de usuario que coincidan con esta expresión regular reciban metadatos bloqueantes al solicitar su página web.

import type { NextConfig } from 'next'

const config: NextConfig = {
  htmlLimitedBots: /MySpecialBot|MyAnotherSpecialBot|SimpleCrawler/,
}

export default config
module.exports = {
  htmlLimitedBots: /MySpecialBot|MyAnotherSpecialBot|SimpleCrawler/,
}

Especificar una configuración htmlLimitedBots anulará la lista predeterminada de Next.js, permitiéndole control total sobre qué agentes de usuario deben optar por este comportamiento. Este es un comportamiento avanzado, y el valor predeterminado debería ser suficiente para la mayoría de los casos.

Orden

Los metadatos se evalúan en orden, comenzando desde el segmento raíz hasta el segmento más cercano al segmento final page.js. Por ejemplo:

  1. app/layout.tsx (Layout raíz)
  2. app/blog/layout.tsx (Layout anidado de blog)
  3. app/blog/[slug]/page.tsx (Página de blog)

Fusión

Siguiendo el orden de evaluación, los objetos Metadata exportados desde múltiples segmentos en la misma ruta se fusionan superficialmente para formar la salida final de metadatos de una ruta. Las claves duplicadas se reemplazan según su orden.

Esto significa que los metadatos con campos anidados como openGraph y robots que se definen en un segmento anterior son sobrescritos por el último segmento que los define.

Sobrescribir campos

app/layout.js
export const metadata = {
  title: 'Acme',
  openGraph: {
    title: 'Acme',
    description: 'Acme es...',
  },
}
app/blog/page.js
export const metadata = {
  title: 'Blog',
  openGraph: {
    title: 'Blog',
  },
}

// Salida:
// <title>Blog</title>
// <meta property="og:title" content="Blog" />

En el ejemplo anterior:

  • title de app/layout.js es reemplazado por title en app/blog/page.js.
  • Todos los campos openGraph de app/layout.js son reemplazados en app/blog/page.js porque app/blog/page.js establece metadatos openGraph. Note la ausencia de openGraph.description.

Si desea compartir algunos campos anidados entre segmentos mientras sobrescribe otros, puede extraerlos en una variable separada:

app/shared-metadata.js
export const openGraphImage = { images: ['http://...'] }
app/page.js
import { openGraphImage } from './shared-metadata'

export const metadata = {
  openGraph: {
    ...openGraphImage,
    title: 'Inicio',
  },
}
app/about/page.js
import { openGraphImage } from '../shared-metadata'

export const metadata = {
  openGraph: {
    ...openGraphImage,
    title: 'Acerca de',
  },
}

En el ejemplo anterior, la imagen OG se comparte entre app/layout.js y app/about/page.js mientras que los títulos son diferentes.

Heredar campos

app/layout.js
export const metadata = {
  title: 'Acme',
  openGraph: {
    title: 'Acme',
    description: 'Acme es...',
  },
}
app/about/page.js
export const metadata = {
  title: 'Acerca de',
}

// Salida:
// <title>Acerca de</title>
// <meta property="og:title" content="Acme" />
// <meta property="og:description" content="Acme es..." />

Notas

  • title de app/layout.js es reemplazado por title en app/about/page.js.
  • Todos los campos openGraph de app/layout.js son heredados en app/about/page.js porque app/about/page.js no establece metadatos openGraph.

Historial de versiones

VersiónCambios
v15.2.0Se introdujo soporte de transmisión para generateMetadata.
v13.2.0viewport, themeColor y colorScheme obsoletos en favor de la configuración viewport.
v13.2.0Se introdujeron metadata y generateMetadata.