generateMetadata
Source URL: https://nextjs.org/docs/app/api-reference/functions/generate-metadata
generateMetadata
Section titled “generateMetadata”You can use the metadata object or the generateMetadata function to define metadata.
The metadata object
Section titled “The metadata object”To define static metadata, export a Metadata object from a layout.js or page.js file.
import type { Metadata } from 'next'
export const metadata: Metadata = { title: '...', description: '...',}
export default function Page() {}export const metadata = { title: '...', description: '...',}
export default function Page() {}See the Metadata Fields for a complete list of supported options.
generateMetadata function
Section titled “generateMetadata function”Dynamic metadata depends on dynamic information, such as the current route parameters, external data, or metadata in parent segments, can be set by exporting a generateMetadata function that returns a Metadata object.
Resolving generateMetadata is part of rendering the page. If the page can be pre-rendered and generateMetadata doesn’t introduce dynamic behavior, the resulting metadata is included in the page’s initial HTML.
Otherwise the metadata resolved from generateMetadata can be streamed after sending the initial UI.
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> { // read route params const { id } = await params
// fetch data const product = await fetch(`https://.../${id}`).then((res) => res.json())
// optionally access and extend (rather than replace) parent metadata 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) { // read route params const { id } = await params
// fetch data const product = await fetch(`https://.../${id}`).then((res) => res.json())
// optionally access and extend (rather than replace) parent metadata const previousImages = (await parent).openGraph?.images || []
return { title: product.title, openGraph: { images: ['/some-specific-page-image.jpg', ...previousImages], }, }}
export default function Page({ params, searchParams }) {}For type completion of params and searchParams, you can type the first argument with PageProps<'/route'> or LayoutProps<'/route'> for pages and layouts respectively.
Good to know:
- Metadata can be added to
layout.jsandpage.jsfiles.- Next.js will automatically resolve the metadata, and create the relevant
<head>tags for the page.- The
metadataobject andgenerateMetadatafunction exports are only supported in Server Components.- You cannot export both the
metadataobject andgenerateMetadatafunction from the same route segment.fetchrequests insidegenerateMetadataare automatically memoized for the same data acrossgenerateMetadata,generateStaticParams, Layouts, Pages, and Server Components.- React
cachecan be used iffetchis unavailable.- File-based metadata has the higher priority and will override the
metadataobject andgenerateMetadatafunction.
Reference
Section titled “Reference”Parameters
Section titled “Parameters”generateMetadata function accepts the following parameters:
-
props- An object containing the parameters of the current route:-
params- An object containing the dynamic route parameters object from the root segment down to the segmentgenerateMetadatais called from. Examples:Route URL paramsapp/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- An object containing the current URL’s search params. Examples:URL searchParams/shop?a=1{ a: '1' }/shop?a=1&b=2{ a: '1', b: '2' }/shop?a=1&a=2{ a: ['1', '2'] }
-
-
parent- A promise of the resolved metadata from parent route segments.
Returns
Section titled “Returns”generateMetadata should return a Metadata object containing one or more metadata fields.
Good to know:
- If metadata doesn’t depend on runtime information, it should be defined using the static
metadataobject rather thangenerateMetadata.fetchrequests are automatically memoized for the same data acrossgenerateMetadata,generateStaticParams, Layouts, Pages, and Server Components. Reactcachecan be used iffetchis unavailable.searchParamsare only available inpage.jssegments.- The
redirect()andnotFound()Next.js methods can also be used insidegenerateMetadata.
Metadata Fields
Section titled “Metadata Fields”The following fields are supported:
The title attribute is used to set the title of the document. It can be defined as a simple string or an optional template object.
String
Section titled “String”export const metadata = { title: 'Next.js',}<title>Next.js</title>default
Section titled “default”title.default can be used to provide a fallback title to child route segments that don’t define a title.
import type { Metadata } from 'next'
export const metadata: Metadata = { title: { default: 'Acme', },}import type { Metadata } from 'next'
export const metadata: Metadata = {}
// Output: <title>Acme</title>template
Section titled “template”title.template can be used to add a prefix or a suffix to titles defined in child route segments.
import type { Metadata } from 'next'
export const metadata: Metadata = { title: { template: '%s | Acme', default: 'Acme', // a default is required when creating a template },}export const metadata = { title: { template: '%s | Acme', default: 'Acme', // a default is required when creating a template },}import type { Metadata } from 'next'
export const metadata: Metadata = { title: 'About',}
// Output: <title>About | Acme</title>export const metadata = { title: 'About',}
// Output: <title>About | Acme</title>Good to know:
title.templateapplies to child route segments and not the segment it’s defined in. This means:
title.defaultis required when you add atitle.template.title.templatedefined inlayout.jswill not apply to atitledefined in apage.jsof the same route segment.title.templatedefined inpage.jshas no effect because a page is always the terminating segment (it doesn’t have any children route segments).title.templatehas no effect if a route has not defined atitleortitle.default.
absolute
Section titled “absolute”title.absolute can be used to provide a title that ignores title.template set in parent segments.
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', },}
// Output: <title>About</title>export const metadata = { title: { absolute: 'About', },}
// Output: <title>About</title>Good to know:
layout.js
title(string) andtitle.defaultdefine the default title for child segments (that do not define their owntitle). It will augmenttitle.templatefrom the closest parent segment if it exists.title.absolutedefines the default title for child segments. It ignorestitle.templatefrom parent segments.title.templatedefines a new title template for child segments.page.js
- If a page does not define its own title the closest parents resolved title will be used.
title(string) defines the routes title. It will augmenttitle.templatefrom the closest parent segment if it exists.title.absolutedefines the route title. It ignorestitle.templatefrom parent segments.title.templatehas no effect inpage.jsbecause a page is always the terminating segment of a route.
description
Section titled “description”export const metadata = { description: 'The React Framework for the Web',}<meta name="description" content="The React Framework for the Web" />Other fields
Section titled “Other fields”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, },}<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
Section titled “metadataBase”metadataBase is a convenience option to set a base URL prefix for metadata fields that require a fully qualified URL.
metadataBaseallows URL-basedmetadatafields defined in the current route segment and below to use a relative path instead of an otherwise required absolute URL.- The field’s relative path will be composed with
metadataBaseto form a fully qualified URL.
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', },}<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" />Good to know:
metadataBaseis typically set in rootapp/layout.jsto apply to URL-basedmetadatafields across all routes.- All URL-based
metadatafields that require absolute URLs can be configured with ametadataBaseoption.metadataBasecan contain a subdomain e.g.https://app.acme.comor base path e.g.https://acme.com/start/from/here- If a
metadatafield provides an absolute URL,metadataBasewill be ignored.- Using a relative path in a URL-based
metadatafield without configuring ametadataBasewill cause a build error.- Next.js will normalize duplicate slashes between
metadataBase(e.g.https://acme.com/) and a relative field (e.g./path) to a single slash (e.g.https://acme.com/path)
URL Composition
Section titled “URL Composition”URL composition favors developer intent over default directory traversal semantics.
- Trailing slashes between
metadataBaseandmetadatafields are normalized. - An “absolute” path in a
metadatafield (that typically would replace the whole URL path) is treated as a “relative” path (starting from the end ofmetadataBase).
For example, given the following 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'),}Any metadata fields that inherit the above metadataBase and set their own value will be resolved as follows:
metadata field | Resolved URL |
|---|---|
/ | https://acme.com |
./ | https://acme.com |
payments | https://acme.com/payments |
/payments | https://acme.com/payments |
./payments | https://acme.com/payments |
../payments | https://acme.com/payments |
https://beta.acme.com/payments | https://beta.acme.com/payments |
openGraph
Section titled “openGraph”export const metadata = { openGraph: { title: 'Next.js', description: 'The React Framework for the Web', url: 'https://nextjs.org', siteName: 'Next.js', images: [ { url: 'https://nextjs.org/og.png', // Must be an absolute URL width: 800, height: 600, }, { url: 'https://nextjs.org/og-alt.png', // Must be an absolute URL width: 1800, height: 1600, alt: 'My custom alt', }, ], videos: [ { url: 'https://nextjs.org/video.mp4', // Must be an absolute URL width: 800, height: 600, }, ], audio: [ { url: 'https://nextjs.org/audio.mp3', // Must be an absolute URL }, ], locale: 'en_US', type: 'website', },}<meta property="og:title" content="Next.js" /><meta property="og:description" content="The React Framework for the 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="My custom alt" /><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" />export const metadata = { openGraph: { title: 'Next.js', description: 'The React Framework for the Web', type: 'article', publishedTime: '2023-01-01T00:00:00.000Z', authors: ['Seb', 'Josh'], },}<meta property="og:title" content="Next.js" /><meta property="og:description" content="The React Framework for the 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" />Good to know:
- It may be more convenient to use the file-based Metadata API for Open Graph images. Rather than having to sync the config export with actual files, the file-based API will automatically generate the correct metadata for you.
robots
Section titled “robots”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, }, },}<meta name="robots" content="index, follow" /><meta name="googlebot" content="index, follow, max-video-preview:-1, max-image-preview:large, max-snippet:-1"/>Good to know: We recommend using the file-based Metadata API for icons where possible. Rather than having to sync the config export with actual files, the file-based API will automatically generate the correct metadata for you.
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', }, },}<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"/>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', }, ], },}<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"/>Good to know: The
msapplication-*meta tags are no longer supported in Chromium builds of Microsoft Edge, and thus no longer needed.
themeColor
Section titled “themeColor”Deprecated: The
themeColoroption inmetadatais deprecated as of Next.js 14. Please use theviewportconfiguration instead.
colorScheme
Section titled “colorScheme”Deprecated: The
colorSchemeoption inmetadatais deprecated as of Next.js 14. Please use theviewportconfiguration instead.
manifest
Section titled “manifest”A web application manifest, as defined in the Web Application Manifest specification.
export const metadata = { manifest: 'https://nextjs.org/manifest.json',}<link rel="manifest" href="https://nextjs.org/manifest.json" />twitter
Section titled “twitter”The Twitter specification is (surprisingly) used for more than just X (formerly known as Twitter).
Learn more about the Twitter Card markup reference.
export const metadata = { twitter: { card: 'summary_large_image', title: 'Next.js', description: 'The React Framework for the Web', siteId: '1467726470533754880', creator: '@nextjs', creatorId: '1467726470533754880', images: ['https://nextjs.org/og.png'], // Must be an absolute URL },}<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="The React Framework for the Web" /><meta name="twitter:image" content="https://nextjs.org/og.png" />export const metadata = { twitter: { card: 'app', title: 'Next.js', description: 'The React Framework for the Web', siteId: '1467726470533754880', creator: '@nextjs', creatorId: '1467726470533754880', images: { url: 'https://nextjs.org/og.png', alt: 'Next.js Logo', }, 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', }, }, },}<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="The React Framework for the Web" /><meta name="twitter:card" content="app" /><meta name="twitter:image" content="https://nextjs.org/og.png" /><meta name="twitter:image:alt" content="Next.js Logo" /><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
Section titled “viewport”Deprecated: The
viewportoption inmetadatais deprecated as of Next.js 14. Please use theviewportconfiguration instead.
verification
Section titled “verification”export const metadata = { verification: { google: 'google', yandex: 'yandex', yahoo: 'yahoo', other: { me: ['my-email', 'my-link'], }, },}<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
Section titled “appleWebApp”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)', }, ], },}<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
Section titled “alternates”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', }, },}<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"/>appLinks
Section titled “appLinks”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, }, },}<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
Section titled “archives”Describes a collection of records, documents, or other materials of historical interest (source).
export const metadata = { archives: ['https://nextjs.org/13'],}<link rel="archives" href="https://nextjs.org/13" />assets
Section titled “assets”export const metadata = { assets: ['https://nextjs.org/assets'],}<link rel="assets" href="https://nextjs.org/assets" />bookmarks
Section titled “bookmarks”export const metadata = { bookmarks: ['https://nextjs.org/13'],}<link rel="bookmarks" href="https://nextjs.org/13" />category
Section titled “category”export const metadata = { category: 'technology',}<meta name="category" content="technology" />facebook
Section titled “facebook”You can connect a Facebook app or Facebook account to your webpage for certain Facebook Social Plugins Facebook Documentation
Good to know: You can specify either appId or admins, but not both.
export const metadata = { facebook: { appId: '12345678', },}<meta property="fb:app_id" content="12345678" />export const metadata = { facebook: { admins: '12345678', },}<meta property="fb:admins" content="12345678" />If you want to generate multiple fb:admins meta tags you can use array value.
export const metadata = { facebook: { admins: ['12345678', '87654321'], },}<meta property="fb:admins" content="12345678" /><meta property="fb:admins" content="87654321" />pinterest
Section titled “pinterest”You can enable or disable Pinterest Rich Pins on your webpage.
export const metadata = { pinterest: { richPin: true, },}<meta name="pinterest-rich-pin" content="true" />All metadata options should be covered using the built-in support. However, there may be custom metadata tags specific to your site, or brand new metadata tags just released. You can use the other option to render any custom metadata tag.
export const metadata = { other: { custom: 'meta', },}<meta name="custom" content="meta" />If you want to generate multiple same key meta tags you can use array value.
export const metadata = { other: { custom: ['meta1', 'meta2'], },}<meta name="custom" content="meta1" /> <meta name="custom" content="meta2" />You can add type safety to your metadata by using the Metadata type. If you are using the built-in TypeScript plugin in your IDE, you do not need to manually add the type, but you can still explicitly add it if you want.
metadata object
Section titled “metadata object”import type { Metadata } from 'next'
export const metadata: Metadata = { title: 'Next.js',}generateMetadata function
Section titled “generateMetadata function”Regular function
Section titled “Regular function”import type { Metadata } from 'next'
export function generateMetadata(): Metadata { return { title: 'Next.js', }}Async function
Section titled “Async function”import type { Metadata } from 'next'
export async function generateMetadata(): Promise<Metadata> { return { title: 'Next.js', }}With segment props
Section titled “With segment props”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) {}With parent metadata
Section titled “With parent metadata”import type { Metadata, ResolvingMetadata } from 'next'
export async function generateMetadata( { params, searchParams }: Props, parent: ResolvingMetadata): Promise<Metadata> { return { title: 'Next.js', }}JavaScript Projects
Section titled “JavaScript Projects”For JavaScript projects, you can use JSDoc to add type safety.
/** @type {import("next").Metadata} */export const metadata = { title: 'Next.js',}Unsupported Metadata
Section titled “Unsupported Metadata”The following metadata types do not currently have built-in support. However, they can still be rendered in the layout or page itself.
| Metadata | Recommendation |
|---|---|
<meta http-equiv="..."> | Use appropriate HTTP Headers via redirect(), Proxy, Security Headers |
<base> | Render the tag in the layout or page itself. |
<noscript> | Render the tag in the layout or page itself. |
<style> | Learn more about styling in Next.js. |
<script> | Learn more about using scripts. |
<link rel="stylesheet" /> | import stylesheets directly in the layout or page itself. |
<link rel="preload /> | Use ReactDOM preload method |
<link rel="preconnect" /> | Use ReactDOM preconnect method |
<link rel="dns-prefetch" /> | Use ReactDOM prefetchDNS method |
Resource hints
Section titled “Resource hints”The <link> element has a number of rel keywords that can be used to hint to the browser that an external resource is likely to be needed. The browser uses this information to apply preloading optimizations depending on the keyword.
While the Metadata API doesn’t directly support these hints, you can use new ReactDOM methods to safely insert them into the <head> of the document.
'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 '...'}<link rel="preload">
Section titled “<link rel="preload">”Start loading a resource early in the page rendering (browser) lifecycle. MDN Docs.
ReactDOM.preload(href: string, options: { as: string })<link rel="preload" href="..." as="..." /><link rel="preconnect">
Section titled “<link rel="preconnect">”Preemptively initiate a connection to an origin. MDN Docs.
ReactDOM.preconnect(href: string, options?: { crossOrigin?: string })<link rel="preconnect" href="..." crossorigin /><link rel="dns-prefetch">
Section titled “<link rel="dns-prefetch">”Attempt to resolve a domain name before resources get requested. MDN Docs.
ReactDOM.prefetchDNS(href: string)<link rel="dns-prefetch" href="..." />Good to know:
- These methods are currently only supported in Client Components, which are still Server Side Rendered on initial page load.
- Next.js in-built features such as
next/font,next/imageandnext/scriptautomatically handle relevant resource hints.
Behavior
Section titled “Behavior”Default Fields
Section titled “Default Fields”There are two default meta tags that are always added even if a route doesn’t define metadata:
- The meta charset tag sets the character encoding for the website.
- The meta viewport tag sets the viewport width and scale for the website to adjust for different devices.
<meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" />Good to know: You can overwrite the default
viewportmeta tag.
Streaming metadata
Section titled “Streaming metadata”Streaming metadata allows Next.js to render and send the initial UI to the browser, without waiting for generateMetadata to complete.
When generateMetadata resolves, the resulting metadata tags are appended to the <body> tag. We have verified that metadata is interpreted correctly by bots that execute JavaScript and inspect the full DOM (e.g. Googlebot).
For HTML-limited bots that can’t execute JavaScript (e.g. facebookexternalhit), metadata continues to block page rendering. The resulting metadata will be available in the <head> tag.
Next.js automatically detects HTML-limited bots by looking at the User Agent header. You can use the htmlLimitedBots option in your Next.js config file to override the default User Agent list.
To fully disable streaming metadata:
import type { NextConfig } from 'next'
const config: NextConfig = { htmlLimitedBots: /.*/,}
export default configmodule.exports = { htmlLimitedBots: /.*/,}Streaming metadata improves perceived performance by reducing TTFB and can help lowering LCP time.
Overriding htmlLimitedBots could lead to longer response times. Streaming metadata is an advanced feature, and the default should be sufficient for most cases.
With Cache Components
Section titled “With Cache Components”When Cache Components is enabled, generateMetadata follows the same rules as other components. If metadata accesses runtime data (cookies(), headers(), params, searchParams) or performs uncached data fetching, it defers to request time.
How Next.js handles this depends on the rest of your page:
- If other parts also defer to request time: Prerendering generates a static shell, and metadata streams in with other deferred content.
- If the page or layout is otherwise fully prerenderable: Next.js requires an explicit choice: cache the data if possible, or signal that deferred rendering is intentional.
Streaming metadata at runtime while the rest of the page is fully prerenderable is not common. To ensure this behavior is intentional, an error is raised indicating which page or layout needs to be handled.
To resolve this, you have two options. If metadata depends on external data but not runtime data, use use cache:
export async function generateMetadata() { 'use cache' const { title, description } = await db.query('site-metadata') return { title, description }}If metadata genuinely requires runtime data, add a dynamic marker component to your page:
import { Suspense } from 'react'import { cookies } from 'next/headers'import { connection } from 'next/server'
export async function generateMetadata() { const token = (await cookies()).get('token')?.value // ... use token to fetch personalized metadata return { title: 'Personalized Title' }}
const Connection = async () => { await connection() return null}
async function DynamicMarker() { return ( <Suspense> <Connection /> </Suspense> )}
export default function Page() { // DO NOT place await connection() here // doing so prevents the article tag content from // being included in the static shell return ( <> <article>Static content</article> <DynamicMarker /> </> )}The DynamicMarker component renders nothing but tells Next.js the page has intentional dynamic content. By wrapping it in Suspense, the static content still prerenders normally.
Ordering
Section titled “Ordering”Metadata is evaluated in order, starting from the root segment down to the segment closest to the final page.js segment. For example:
app/layout.tsx(Root Layout)app/blog/layout.tsx(Nested Blog Layout)app/blog/[slug]/page.tsx(Blog Page)
Merging
Section titled “Merging”Following the evaluation order, Metadata objects exported from multiple segments in the same route are shallowly merged together to form the final metadata output of a route. Duplicate keys are replaced based on their ordering.
This means metadata with nested fields such as openGraph and robots that are defined in an earlier segment are overwritten by the last segment to define them.
Overwriting fields
Section titled “Overwriting fields”export const metadata = { title: 'Acme', openGraph: { title: 'Acme', description: 'Acme is a...', },}export const metadata = { title: 'Blog', openGraph: { title: 'Blog', },}
// Output:// <title>Blog</title>// <meta property="og:title" content="Blog" />In the example above:
titlefromapp/layout.jsis replaced bytitleinapp/blog/page.js.- All
openGraphfields fromapp/layout.jsare replaced inapp/blog/page.jsbecauseapp/blog/page.jssetsopenGraphmetadata. Note the absence ofopenGraph.description.
If you’d like to share some nested fields between segments while overwriting others, you can pull them out into a separate variable:
export const openGraphImage = { images: ['http://...'] }import { openGraphImage } from './shared-metadata'
export const metadata = { openGraph: { ...openGraphImage, title: 'Home', },}import { openGraphImage } from '../shared-metadata'
export const metadata = { openGraph: { ...openGraphImage, title: 'About', },}In the example above, the OG image is shared between app/layout.js and app/about/page.js while the titles are different.
Inheriting fields
Section titled “Inheriting fields”export const metadata = { title: 'Acme', openGraph: { title: 'Acme', description: 'Acme is a...', },}export const metadata = { title: 'About',}
// Output:// <title>About</title>// <meta property="og:title" content="Acme" />// <meta property="og:description" content="Acme is a..." />Notes
titlefromapp/layout.jsis replaced bytitleinapp/about/page.js.- All
openGraphfields fromapp/layout.jsare inherited inapp/about/page.jsbecauseapp/about/page.jsdoesn’t setopenGraphmetadata.
Version History
Section titled “Version History”| Version | Changes |
|---|---|
v15.2.0 | Introduced streaming support to generateMetadata. |
v13.2.0 | viewport, themeColor, and colorScheme deprecated in favor of the viewport configuration. |
v13.2.0 | metadata and generateMetadata introduced. |
Next Steps
Section titled “Next Steps”View all the Metadata API options.
- Metadata Files
- API documentation for the metadata file conventions.
- generateViewport
- API Reference for the generateViewport function.
- Cache Components
- Learn how to use Cache Components and combine the benefits of static and dynamic rendering.
- cacheComponents
- Learn how to enable the cacheComponents flag in Next.js.