LearnAI ToolsCareerPractice BuildsPlayContact
Lesson 3917 min read

Metadata API & SEO

Control titles, descriptions, Open Graph tags, and more per route using the built-in Metadata API.

Static Metadata

Any page.tsx or layout.tsx can export a metadata object to define its <title>, description, and other head tags — no manual <head> manipulation required.

// app/about/page.tsx
export const metadata = {
title: 'About Us | PrograMinds',
description: 'Learn about our mission to make programming education accessible.',
};
export default function AboutPage() {
return <h1>About Us</h1>;
}

Dynamic Metadata with generateMetadata

For metadata that depends on fetched data — like a blog post's own title — export an async generateMetadata function instead of a static object.

// app/blog/[slug]/page.tsx
export async function generateMetadata({ params }) {
const post = await getPostBySlug(params.slug);
return {
title: `${post.title} | PrograMinds Blog`,
description: post.excerpt,
};
}
Shared Data Fetching

If generateMetadata and the page component both need the same data, wrap the fetch in React's cache() function — Request Memoization then ensures the underlying fetch only actually runs once per request.

Metadata Inheritance

Metadata defined in a layout applies to every page beneath it, and a page's own metadata merges with (and can override) its parent layout's values — you don't need to repeat shared fields like a site-wide title template on every page.

// app/layout.tsx
export const metadata = {
title: { template: '%s | PrograMinds', default: 'PrograMinds' },
};
// A page setting title: 'About Us' renders as "About Us | PrograMinds"

Open Graph & Social Sharing

The same metadata object controls how a page looks when shared on social platforms, via the openGraph field.

export const metadata = {
title: 'PrograMinds — Learn to Code',
openGraph: {
title: 'PrograMinds — Learn to Code',
description: 'Free, comprehensive programming courses.',
images: ['/og-image.png'],
},
};

Sitemaps & robots.txt

Next.js also lets you generate sitemap.xml and robots.txt dynamically, as special files, so search engines get accurate crawling instructions straight from your app's own data.

// app/sitemap.ts
export default async function sitemap() {
const posts = await getAllPosts();
return posts.map(post => ({
url: `https://example.com/blog/${post.slug}`,
lastModified: post.updatedAt,
}));
}

Common Beginner Mistakes

Manually adding <title> and <meta> tags inside a page's JSX

This bypasses the Metadata API entirely and can conflict with it — use the exported metadata object or generateMetadata instead.

Forgetting metadata is a Server-only export

metadata and generateMetadata can only be exported from Server Components, not from files marked "use client".

FAQs

Yes, generateMetadata can read request-specific data just like a page can, though doing so makes that route dynamically rendered.

For most needs, yes — the built-in Metadata API covers titles, Open Graph, Twitter cards, and more without an extra dependency.

Summary

The Metadata API gives every route precise, mergeable control over its SEO tags, with dynamic support for content-driven pages. Next, you'll look at the different ways to style a Next.js application.

Next Lesson →

Styling in Next.js