Next.js: Add metadata
15 June 2025 (Updated 15 June 2025)
On this page
Add static metadata
Export a metadata
object from a layout or page file:
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'My Blog',
description: '...',
}
export default function Page() {}
Add dynamic metadata
import type { Metadata, ResolvingMetadata } from 'next'
type Props = {
params: Promise<{ slug: string }>
searchParams: Promise<{ [key: string]: string | string[] | undefined }>
}
export async function generateMetadata(
{ params, searchParams }: Props,
parent: ResolvingMetadata
): Promise<Metadata> {
const slug = (await params).slug
// fetch post information
const post = await fetch(`https://api.vercel.app/blog/${slug}`).then((res) =>
res.json()
)
return {
title: post.title,
description: post.description,
}
}
export default function Page({ params, searchParams }: Props) {}
If your generateMetadata
function fetches the same data as the page or layout, you might want to use React’s cache
function to deduplicate the requests:
import { cache } from 'react'
import { db } from '@/app/lib/db'
// getPost will be used twice, but execute only once
export const getPost = cache(async (slug: string) => {
const res = await db.query.posts.findFirst({ where: eq(posts.slug, slug) })
return res
})
Then, in your generateMetadata
function and page component:
import { getPost } from '@/app/lib/data'
export async function generateMetadata({
params,
}: {
params: { slug: string }
}) {
const post = await getPost(params.slug)
return {
title: post.title,
description: post.description,
}
}
export default async function Page({ params }: { params: { slug: string } }) {
const post = await getPost(params.slug)
return <div>{post.title}</div>
}
Tagged:
Next.js recipes