Metadata and SEO in Next.js

intermediate
12 min

Metadata and SEO in Next.js

Server rendering gives Next.js apps a head start with search engines, but real SEO requires correct metadata: titles, descriptions, social cards, sitemaps, and structured data. The App Router provides a dedicated Metadata API that generates all of it from your components. This lesson covers static metadata, dynamic metadata, and the special SEO files Next.js can build for you.

Static Metadata with the metadata Export

Any layout.js or page.js can export a metadata object:

jsx
// app/about/page.js export const metadata = { title: 'About Us | CodeYourCraft', description: 'Learn about the CodeYourCraft mission: free, practical web development tutorials.', }; export default function AboutPage() { return <h1>About Us</h1>; }

Next.js renders these into head tags on the server, so crawlers see them without executing JavaScript. Metadata merges down the tree: a layout can set defaults and a page can override them.

Title Templates

Avoid repeating your site name in every page title by defining a template in the root layout:

jsx
// app/layout.js export const metadata = { title: { template: '%s | CodeYourCraft', default: 'CodeYourCraft - Learn Web Development', }, description: 'Free coding tutorials for modern web developers.', };

Now a page exporting title: 'Next.js Course' renders as 'Next.js Course | CodeYourCraft' automatically.

Dynamic Metadata with generateMetadata

For dynamic routes, metadata depends on data. Export an async generateMetadata function:

jsx
// app/blog/[slug]/page.js export async function generateMetadata({ params }) { const { slug } = await params; const post = await getPost(slug); if (!post) return { title: 'Post Not Found' }; return { title: post.title, description: post.excerpt, openGraph: { title: post.title, description: post.excerpt, images: [{ url: post.coverImage }], type: 'article', }, }; }

Fetch calls here are deduplicated with the page's own fetches, so requesting the post twice costs one network call. The openGraph block controls how links look when shared on social platforms; add a twitter block with card: 'summary_large_image' for the best rendering on X.

Sitemaps and Robots Files from Code

Instead of hand-maintaining XML, generate it. A sitemap.js file at the app root produces /sitemap.xml:

js
// app/sitemap.js import { getAllPosts } from '@/lib/posts'; export default async function sitemap() { const posts = await getAllPosts(); const postUrls = posts.map((p) => ({ url: 'https://example.com/blog/' + p.slug, lastModified: p.updatedAt, })); return [ { url: 'https://example.com', lastModified: new Date() }, ...postUrls, ]; }

Similarly, app/robots.js generates robots.txt, and opengraph-image.js can even generate social card images with code.

Structured Data with JSON-LD

Rich results (star ratings, article cards) come from structured data. Render a JSON-LD script in your page:

jsx
const jsonLd = { '@context': 'https://schema.org', '@type': 'Article', headline: post.title, datePublished: post.publishedAt, }; <script type='application/ld+json' dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }} />

SEO Fundamentals Beyond Metadata

Metadata cannot rescue a slow or confusing site. The rest of this course already covered the other pillars: server rendering for crawlable HTML, notFound() for correct 404 status codes, semantic headings, descriptive link text, and the image and font optimization from Lesson 11 that keeps Core Web Vitals green.

Key Takeaways

  • Export metadata for static pages and generateMetadata for dynamic ones.
  • Title templates keep branding consistent without repetition.
  • openGraph and twitter fields control social sharing previews.
  • Generate sitemap.xml and robots.txt from code so they never go stale.
  • JSON-LD structured data unlocks rich search results.

Next up: the final lesson - building for production, deploying your app, and a checklist of best practices.

Metadata and SEO in Next.js - Next.js | CodeYourCraft | CodeYourCraft