Metadata & SEO Overview
<head> tags yourself — usually with a third-party library, careful ordering, and a lot of copy-pasted boilerplate for every page. The App Router replaces all of that with a built-in Metadata API: you describe what a page's title, description, and social preview should be as plain data, and Next.js generates the correct, deduplicated <head> tags for you.title and description to decide how to list your page in results. Social platforms and chat apps (Slack, X, LinkedIn, WhatsApp) read your Open Graph and Twitter Card tags to build the preview card shown when someone shares your link. Getting either wrong — or leaving them blank — means a worse-looking result in search, and a blank or broken preview card everywhere else.Two ways to define metadata
layout.tsx or page.tsx) can export metadata in one of two forms, depending on whether the content is known ahead of time or depends on data you fetch.Approach | Export | Use when |
|---|---|---|
Static metadata | a plain | The title/description are fixed text you can write by hand — an About page, a pricing page, the homepage. |
Dynamic metadata | an async | The title/description depend on fetched data — a blog post, a product page, a user profile. |
Static — app/about/page.tsx
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'About Us',
description: 'Learn what we do and why we do it.',
}
export default function AboutPage() {
return <h1>About Us</h1>
}Dynamic — app/blog/[slug]/page.tsx
import type { Metadata } from 'next'
export async function generateMetadata({
params,
}: {
params: { slug: string }
}): Promise<Metadata> {
const post = await getPost(params.slug)
return {
title: post.title,
description: post.excerpt,
}
}
export default async function BlogPostPage({
params,
}: {
params: { slug: string }
}) {
const post = await getPost(params.slug)
return <article>{post.title}</article>
}generateMetadata— are covered in their own dedicated pages next in this section.Metadata is merged down the route tree
layout.tsx can export metadata too, and it applies to every page nested inside it. Next.js walks the route tree from the root layout down to the matched page, merging metadata objects along the way. A field a page defines overrides the same field from a parent layout; a field the page leaves out is inherited unchanged.app/layout.tsx
export const metadata = {
title: 'My Site',
description: 'The default description for every page on the site.',
}app/blog/page.tsx
export const metadata = {
title: 'Blog',
// description is not set here, so the root layout's
// description is inherited as-is.
}title when using a template, covered on the next page) have their own special composition rules rather than a simple overwrite. Arrays and objects such as openGraph are replaced wholesale by the closest segment that defines them, not deep-merged key by key.The Metadata API replaces manual
<head>management with declarative data.Static
metadataobjects suit fixed content;generateMetadata()suits content built from fetched data.Metadata can be exported from both
layout.tsxandpage.tsx, and is merged down the tree.A closer segment overrides the same metadata field from a parent; unset fields are inherited.
Getting this right improves both search engine listings and social share previews.