On this page
Metadata and SEO
Next.js provides a Metadata API for managing <title>, <meta>, Open Graph tags, and more — without manually editing HTML <head> elements.
Static Metadata
Export a metadata object from any layout.tsx or page.tsx:
// app/about/page.tsx
import type { Metadata } from 'next';
export const metadata: Metadata = {
title: 'About Us',
description: 'Learn about our team and mission.',
};
export default function AboutPage() {
return <h1>About Us</h1>;
}
Child pages inherit metadata from parent layouts. Page metadata overrides layout metadata.
Root Layout Metadata
Set defaults in the root layout:
export const metadata: Metadata = {
title: {
default: 'My Next.js App',
template: '%s | My Next.js App',
},
description: 'A modern web application built with Next.js.',
metadataBase: new URL('https://myapp.com'),
};
The template adds a suffix to child page titles: “About Us | My Next.js App”.
Dynamic Metadata with generateMetadata
Generate metadata from fetched data:
// app/blog/[slug]/page.tsx
import type { Metadata } from 'next';
type Props = { params: Promise<{ slug: string }> };
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const { slug } = await params;
const post = await getPost(slug);
return {
title: post.title,
description: post.excerpt,
openGraph: {
title: post.title,
description: post.excerpt,
images: [{ url: post.coverImage, width: 1200, height: 630 }],
},
};
}
export default async function BlogPost({ params }: Props) {
const { slug } = await params;
const post = await getPost(slug);
return <article><h1>{post.title}</h1></article>;
}
Open Graph and Twitter Cards
Control social sharing previews:
export const metadata: Metadata = {
openGraph: {
title: 'Product Page',
description: 'Check out our latest product.',
url: 'https://myapp.com/products/widget',
siteName: 'My App',
images: [{ url: 'https://myapp.com/og-image.png', width: 1200, height: 630 }],
type: 'website',
},
twitter: {
card: 'summary_large_image',
title: 'Product Page',
images: ['https://myapp.com/og-image.png'],
},
};
Robots and Canonical URLs
Set robots: { index: true, follow: true } and alternates: { canonical: '...' } in metadata. Use robots: { index: false } to hide pages from search engines.
Sitemap and robots.txt
Generate a sitemap programmatically:
// app/sitemap.ts
import type { MetadataRoute } from 'next';
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
const posts = await getAllPosts();
return [
{ url: 'https://myapp.com', lastModified: new Date(), priority: 1 },
...posts.map(post => ({
url: `https://myapp.com/blog/${post.slug}`,
lastModified: post.updatedAt,
priority: 0.6,
})),
];
}
Available at https://myapp.com/sitemap.xml.
// app/robots.ts
export default function robots() {
return {
rules: { userAgent: '*', allow: '/', disallow: ['/admin/'] },
sitemap: 'https://myapp.com/sitemap.xml',
};
}
Next: add authentication with Auth.js.