generateMetadata Function
The static
metadata object works when a page's title and description are known ahead of time. When they aren't — a blog post whose title comes from a CMS, a product page whose description comes from a database — export an async generateMetadata function instead. It receives the same params and searchParams as the page itself, so it can fetch exactly the data it needs to build the metadata object.Signature
Function signature
TSX
import type { Metadata } from 'next'
export async function generateMetadata({
params,
searchParams,
}: {
params: { slug: string }
searchParams: { [key: string]: string | string[] | undefined }
}): Promise<Metadata> {
// ...fetch data using params.slug...
return {
title: '...',
description: '...',
}
}Worked example: a blog post
app/blog/[slug]/page.tsx
TSX
import type { Metadata } from 'next'
import { getPost } from '@/libs/posts'
export async function generateMetadata({
params,
}: {
params: { slug: string }
}): Promise<Metadata> {
const post = await getPost(params.slug)
if (!post) {
return { title: 'Post not found' }
}
return {
title: post.title,
description: post.excerpt,
openGraph: {
title: post.title,
description: post.excerpt,
images: [post.coverImage],
type: 'article',
publishedTime: post.publishedAt,
},
}
}
export default async function BlogPostPage({
params,
}: {
params: { slug: string }
}) {
const post = await getPost(params.slug)
if (!post) return <p>Post not found</p>
return (
<article>
<h1>{post.title}</h1>
<div>{post.content}</div>
</article>
)
}Note
Both
generateMetadata and the page component call getPost(params.slug) here, but Next.js automatically memoizes identical fetch requests made during the same render pass. If getPost uses the built-in fetch (or you wrap it with React's cache()), the underlying data is only fetched once even though it's "called" from two different functions — you don't need to hand-roll caching to avoid the duplicate request.This is a meaningful advantage over the older Pages Router pattern, where fetching the same data for both the head tags and the page body typically meant threading props through
getServerSideProps manually.Resolving relative to a parent
generateMetadata also receives a parent argument — a promise resolving to the metadata resolved by the nearest parent segment — which is useful when you want to extend rather than fully replace inherited fields.Extending parent metadata
TSX
export async function generateMetadata(
{ params }: { params: { slug: string } },
parent: ResolvingMetadata
) {
const parentMetadata = await parent
const post = await getPost(params.slug)
return {
...parentMetadata,
title: post.title,
}
}Tip
Reach for the static
metadata object first — it's simpler and requires no data fetching. Switch to generateMetadata only once a page's metadata truly depends on params, searchParams, or an external data source.generateMetadatais an async function that returns aMetadataobject, used when metadata depends on runtime data.It receives the same
paramsandsearchParamsas the page component.Next.js memoizes identical
fetchcalls within a render pass, so calling the same data function from bothgenerateMetadataand the page doesn't duplicate the network request.An optional
parentargument resolves to the parent segment's metadata, useful for extending rather than replacing it.Prefer the static
metadataobject when possible; usegenerateMetadataonly when the values genuinely depend on fetched data.