Next.js SEO with the Metadata API: A Complete Guide
From static titles to dynamic Open Graph images, sitemaps and structured data — everything the Metadata API gives you and how to use it properly.
Next.js server-renders your pages, which means search engines see real HTML rather than an empty shell — that is the easy half of SEO sorted. The other half is metadata: titles, descriptions, canonical URLs, social cards and structured data. The App Router's Metadata API handles nearly all of it through typed exports rather than hand-rolled head tags, and it deduplicates and orders everything for you.
Static metadata in layouts and pages
For pages whose metadata never changes, export a metadata object. Layouts provide defaults that pages inherit and can override. The title template is the detail most teams miss — define it once in the root layout and every child page gets consistent branding without repeating the suffix.
// app/layout.tsx
import type { Metadata } from 'next';
export const metadata: Metadata = {
metadataBase: new URL('https://strcli.com'),
title: {
default: 'STRCLI',
template: '%s | STRCLI',
},
description: 'Full-stack web development from the UK.',
};Setting metadataBase matters more than it looks: it lets you use relative URLs everywhere else — Open Graph images, canonicals, alternates — and Next.js resolves them correctly per environment.
Dynamic metadata with generateMetadata
Content-driven pages need generateMetadata, an async function that receives route params and returns the same Metadata shape. Fetches made here are deduplicated with the ones in your page component, so asking for the same post twice costs one request, not two.
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const { slug } = await params;
const post = await getPost(slug);
if (!post) return { title: 'Not found' };
return {
title: post.title,
description: post.excerpt,
alternates: { canonical: '/blog/' + post.slug },
};
}Open Graph and Twitter cards
Social previews are set through the openGraph and twitter fields, and the images deserve real effort — they are the first thing anyone sees when a link is shared. You can point at static files, but the file-convention route is stronger: drop an opengraph-image.tsx in a route segment and generate the image with ImageResponse, pulling in the actual page title. Every article gets a bespoke card with zero design bottleneck.
Keep Open Graph descriptions independent of meta descriptions where it helps: the former is written for a human deciding whether to click in a feed, the latter for a search results page. They are allowed to differ.
Sitemaps, robots and canonicals
The same file-convention approach covers the plumbing. A sitemap.ts at the app root exports a function returning your URLs — fetch them from the CMS so the sitemap is always current — and robots.ts does the same for crawler rules.
- Generate the sitemap from your data source, never maintain it by hand
- Set a canonical on every indexable page, especially where filters or query strings create duplicate URLs
- Use robots metadata to noindex thin pages such as internal search results
- Check hreflang alternates if you serve multiple locales
Structured data
The Metadata API does not cover JSON-LD, but the pattern is simple: build the object in your Server Component and render it into a script tag with type application/ld+json. Article, Product, Organisation and BreadcrumbList cover most sites. Validate with Google's Rich Results Test rather than assuming — silent schema errors are common.
Verify it in the wild
Metadata work is only done when you have seen it live. Check rendered pages with view-source rather than the element inspector, because crawlers read the served HTML, not the hydrated DOM. Then watch Search Console for coverage and enhancement reports, and paste key URLs into the social platforms' debuggers — the sharing card you imagine and the one Slack or LinkedIn actually renders are not always the same thing.
Good technical SEO in Next.js is mostly about doing unglamorous things consistently. The Metadata API removes the excuses: the tools are typed, colocated and deduplicated, so the remaining work is editorial discipline.