This page covers all Config-based Metadata options with generateMetadata and the static metadata object.
import { Metadata } from 'next'
// either Static metadata
export const metadata: Metadata = {
title: '...',
}
// or Dynamic metadata
export async function generateMetadata({ params }) {
return {
title: '...',
}
}Good to know:
- The
metadataobject andgenerateMetadatafunction exports are only supported in Server Components.- You cannot export both the
metadataobject andgenerateMetadatafunction from the same route segment.
To define static metadata, export a Metadata object from a layout.js or page.js file.
import { Metadata } from 'next'
export const metadata: Metadata = {
title: '...',
description: '...',
}
export default function Page() {}See the Metadata Fields for a complete list of supported options.
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.
import { Metadata, ResolvingMetadata } from 'next'
type Props = {
params: { id: string }
searchParams: { [key: string]: string | string[] | undefined }
}
export async function generateMetadata(
{ params, searchParams }: Props,
parent: ResolvingMetadata
): Promise<Metadata> {
// read route params
const id = params.id
// 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) {}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 segment generateMetadata is called from. Examples:
| Route | URL | params |
|---|---|---|
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 - 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.
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.
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.
export const metadata = {
title: 'Next.js',
}<title>Next.js</title>
import { Metadata } from 'next'
export const metadata: Metadata = {
title: {
template: '...',
default: '...',
absolute: '...',
},
}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>title.template can be used to add a prefix or a suffix to titles defined in child route segments.
import { Metadata } from 'next'
export const metadata: Metadata = {
title: {
template: '%s | Acme',
default: 'Acme', // a default is required when creating a template
},
}import { Metadata } from 'next'
export const metadata: 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.
title.absolute can be used to provide a title that ignores title.template set in parent segments.
import { Metadata } from 'next'
export const metadata: Metadata = {
title: {
template: '%s | Acme',
},
}import { Metadata } from 'next'
export const metadata: 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.
export const metadata = {
description: 'The React Framework for the Web',
}<meta name="description" content="The React Framework for the Web" />
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 is a convenience option to set a base URL prefix for metadata fields that require a fully qualified URL.
metadataBase allows URL-based metadata fields defined in the current route segment and below to use a relative path instead of an otherwise required absolute URL.metadataBase to form a fully qualified URL.metadataBase is automatically populated with a default value.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)
If not configured, metadataBase has a default value
VERCEL_URL is detected: https://${process.env.VERCEL_URL} otherwise it falls back to http://localhost:${process.env.PORT || 3000}.URL composition favors developer intent over default directory traversal semantics.
metadataBase and metadata fields are normalized.metadata field (that typically would replace the whole URL path) is treated as a "relative" path (starting from the end of metadataBase).For example, given the following metadataBase:
import { Metadata } from 'next'
export const metadata: 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 |
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',
},
],
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:url" content="https://nextjs.org/og.png" /> <meta property="og:image:width" content="800" /> <meta property="og:image:height" content="600" /> <meta property="og:image:url" 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: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.
import type { Metadata } from 'next'
export const metadata: Metadata = {
robots: {
index: false,
follow: true,
nocache: true,
googleBot: {
index: true,
follow: false,
noimageindex: true,
'max-video-preview': -1,
'max-image-preview': 'large',
'max-snippet': -1,
},
},
}
<meta name="robots" content="noindex, follow, nocache" /> <meta name="googlebot" content="index, nofollow, noimageindex, 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.
Deprecated: The
themeColoroption inmetadatais deprecated as of Next.js 14. Please use theviewportconfiguration instead.
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" />
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" />
Deprecated: The
viewportoption inmetadatais deprecated as of Next.js 14. Please use theviewportconfiguration instead.
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" />
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="apple-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" />
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" />
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" />
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" />
export const metadata = {
assets: ['https://nextjs.org/assets'],
}<link rel="assets" href="https://nextjs.org/assets" />
export const metadata = {
bookmarks: ['https://nextjs.org/13'],
}<link rel="bookmarks" href="https://nextjs.org/13" />
export const metadata = {
category: 'technology',
}<meta name="category" content="technology" />
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" />
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(), Middleware, 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 |
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 null
}<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">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">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.- React 18.3 does not yet include type definitions for
ReactDOM.preload,ReactDOM.preconnect, andReactDOM.preconnectDNS. You can use// @ts-ignoreas a temporary solution to avoid type errors.
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.
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'Next.js',
}
import type { Metadata } from 'next'
export function generateMetadata(): Metadata {
return {
title: 'Next.js',
}
}
import type { Metadata } from 'next'
export async function generateMetadata(): Promise<Metadata> {
return {
title: 'Next.js',
}
}
import type { Metadata } from 'next'
type Props = {
params: { id: string }
searchParams: { [key: string]: string | string[] | undefined }
}
export function generateMetadata({ params, searchParams }: Props): Metadata {
return {
title: 'Next.js',
}
}
export default function Page({ params, searchParams }: Props) {}
import type { Metadata, ResolvingMetadata } from 'next'
export async function generateMetadata(
{ params, searchParams }: Props,
parent: ResolvingMetadata
): Promise<Metadata> {
return {
title: 'Next.js',
}
}
For JavaScript projects, you can use JSDoc to add type safety.
/** @type {import("next").Metadata} */
export const metadata = {
title: 'Next.js',
}
| Version | Changes |
|---|---|
v13.2.0 |
viewport, themeColor, and colorScheme deprecated in favor of the viewport configuration. |
v13.2.0 |
metadata and generateMetadata introduced. |
© 2024 Vercel, Inc.
Licensed under the MIT License.
https://nextjs.org/docs/app/api-reference/functions/generate-metadata