Note: Built-in support for SEO through metadata, introduced in
13.2
, replaces the previoushead.js
implementation. Thehead.js
special file was deprecated in13.2
and removed in a future version. View migration guide.
Metadata can be used in any page.js
or layout.js
Server Component. This API reference includes every possible option for the returned metadata object.
// Static metadata
export const metadata = {
title: '...',
};
// or dynamic metadata
export async function generateMetadata({ params, searchParams }) {
return { title: '...' };
}
Both static and dynamic metadata through generateMetadata
are only supported in Server Components.
Each metadata
or generateMetadata
export defined in the layout
/page
path will be evaluated in order starting from the highest level, and ending with the export closest to the page
.
For example:
app/layout.tsx
(Root Layout)app/page/blog/layout.tsx
(Blog Layout)app/page/blog/[slug]/page.tsx
(Blog Page)Objects are shallow merged together to form the final metadata output. Duplicate keys are replaced based on their ordering. Learn more about pages and layouts.
generateMetadata
You can use generateMetadata
to fetch
metadata that requires dynamic values. Similar to page
, generateMetadata
has access to the params
and searchParams
props.
export async function generateMetadata({
params,
searchParams,
}: {
params: { id: string },
searchParams: { [key: string]: string | string[] | undefined };
}) {
// For /products/123, params.id is "123"
// For /products/123?color=black, searchParams.color is "black"
// The return value is the metadata object
return { title: '...' };
}
When fetching data, the fetch
response is cached and reused between both generateMetadata
and the Page
Server Component below, resulting in a single fetch
request. If you cannot use fetch
directly, you can use cache
. Learn more.
import type { Metadata } from 'next'
async function getProduct(id) {
const res = await fetch(`https://.../api/products/${id}`);
return res.json();
}
export async function generateMetadata({ params }): Promise<Metadata> {
const product = await getProduct(params.id);
return { title: product.title }
}
export default async function Page({ params }) {
const product = await getProduct(params.id);
// ...
}
The following Next.js methods can also be used inside generateMetadata
:
There are two default meta
tags:
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
These are always added even if a route doesn't define metadata.
title
export const metadata = {
title: 'Next.js',
};
<title>Next.js</title>
You can use a template to add a prefix or suffix to the title
of a page. Any layout
can add the title template
, including deeper nested layouts.
export const metadata = {
title: {
default: 'Acme',
template: '%s | Acme',
},
}
// Output: <title>Acme</title>
export const metadata = {
// Default title is used from the layout
description: 'ACME is a...',
}
// Output: <title>About | Acme</title>
export const metadata = {
title: 'About',
}
description
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' }],
colorScheme: 'dark',
creator: 'Jiachi Liu',
publisher: 'Sebastian Markbåge',
alternates: {},
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">
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',
width: 800,
height: 600,
},
{
url: 'https://nextjs.org/og-alt.png',
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" />
robots
export const 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"
/>
icons
Next.js has built-in support for favicon detection. If you place a favicon.ico
file in the app/
directory, it will automatically add the correct link
tags to your page.
You do not need to manually add the icon
key below, however, it is still supported in case your favicon is not stored inside the directory.
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')],
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="apple-touch-icon" href="/apple-icon.png" />
<link
rel="apple-touch-icon-precomposed"
href="/apple-touch-icon-precomposed.png"
/>
<link rel="icon" href="https://example.com/icon.png" />
<link
rel="apple-touch-icon"
href="/apple-icon-x3.png"
sizes="180x180"
type="image/png"
/>
Note: The
msapplication-*
meta tags are no longer supported in Chromium builds of Microsoft Edge, and thus no longer needed.
themeColor
Learn more about theme-color.
Simple theme color
export const metadata = {
themeColor: 'black',
};
<meta name="theme-color" content="black" />
With media attribute
export const metadata = {
themeColor: [
{ media: '(prefers-color-scheme: light)', color: 'cyan' },
{ media: '(prefers-color-scheme: dark)', color: 'black' },
],
};
<meta name="theme-color" media="(prefers-color-scheme: light)" content="cyan" />
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="black" />
manifest
The manifest.json
file is the only file that every extension using WebExtension APIs must contain.
export const metadata = {
manifest: 'https://nextjs.org/manifest.json',
};
<link rel="manifest" href="https://nextjs.org/manifest.json" />
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'],
},
};
<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
// Note: This is the same values as the default value set
// You likely don't need to change this, but it's included for completeness
export const metadata = {
viewport: {
width: 'device-width',
initialScale: 1,
maximumScale: 1,
},
};
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
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
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">
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
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
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
export const metadata = {
assets: ['https://nextjs.org/assets'],
};
<link rel="assets" href="https://nextjs.org/assets">
bookmarks
export const metadata = {
bookmarks: ['https://nextjs.org/13'],
};
<link rel="bookmarks" href="https://nextjs.org/13">
category
export const metadata = {
category: 'technology',
};
<meta name="category" content="technology">
other
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">
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="..."> | Render the tag in the layout or page itself. |
<meta charset="..."> | Render the tag in the layout or page itself. |
<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 /> | Learn more about using scripts. |
<link rel="preconnect" /> | Learn more about using scripts. |
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',
};
For JavaScript projects, you can use JSDoc to add type safety.
/** @type {import("next").Metadata} */
export const metadata = {
title: 'Hi',
};