Guides
How to Generate Metadata in Next.js
The Metadata API lets every route define its own title, description, and Open Graph tags — either as a static object or dynamically, based on fetched data.
Prerequisites
- arrow_rightA Next.js App Router project
Step 1: Static Metadata
app/about/page.tsx
import type { Metadata } from 'next';
export const metadata: Metadata = {
title: 'About | My Site',
description: 'A short, specific description under 160 characters.',
};Step 2: Dynamic Metadata
app/blog/[slug]/page.tsx
export async function generateMetadata({ params }: { params: { slug: string } }): Promise<Metadata> {
const post = await getPost(params.slug);
return {
title: post.title,
description: post.excerpt,
openGraph: { title: post.title, description: post.excerpt, images: [post.image] },
};
}Step 3: Set a Site-Wide Default in the Root Layout
Metadata defined in app/layout.tsx acts as a fallback and can use title.template to append a consistent site name to every page's title automatically.
Common Errors
- arrow_rightEvery page shows the same title — a static metadata export in a layout is overriding page-level metadata; check the specificity of where each is defined
- arrow_rightgenerateMetadata not running — confirm it's exported from a page.tsx or layout.tsx file, not a regular component
- arrow_rightOpen Graph image not showing on social platforms — confirm the image URL is absolute (starts with https://), not relative
Frequently Asked Questions
Can metadata be generated from a database?add
Yes — generateMetadata is an async function, so it can fetch the same data the page itself uses (ideally with request memoization so it's not fetched twice).
How long should a meta description be?add
Roughly under 160 characters so it doesn't get truncated in search results, though search engines sometimes rewrite descriptions regardless of length.
Do I need separate Open Graph and Twitter tags?add
Twitter reads Open Graph tags by default if twitter:card is set to summary_large_image; separate twitter: tags are only needed if you want Twitter-specific overrides.