Next.js

Next.js Metadata & SEO๐Ÿ‘จโ€๐Ÿ’ป

Next.js ships with the best SEO developer experience of any framework. Instead of manually wiring <meta> tags into <head>, you export a typed metadata object or an async generateMetadata function, and Next.js handles the rest โ€” deduplication, merging across layouts, streaming when needed, and generating the correct <head> output. OG images, sitemaps, and robots.txt are file-convention based: drop a sitemap.ts in your app/ directory and you have a dynamic sitemap. No plugins, no config files, no third-party dependencies.

This approach means SEO is not an afterthought bolted onto your app โ€” it is built into the routing layer. Every page.tsx and layout.tsx can own its metadata, and the framework merges everything together following a clear parent-to-child resolution order. If you have worked with Helmet in React or hand-rolled meta tags in a SPA, this will feel like a different world.

Key Takeaways

  • 1Export a static `metadata` object from any `layout.tsx` or `page.tsx` โ€” Next.js resolves and deduplicates tags across the entire route tree automatically
  • 2Use `generateMetadata` for dynamic routes (product pages, blog posts) โ€” it receives route params, can fetch data, and supports streaming metadata after initial HTML is sent
  • 3Title templates (`title: { template: '%s | MyApp', default: 'MyApp' }`) in layouts apply a consistent suffix/prefix to every child page without manual repetition
  • 4OG images can be static files (`opengraph-image.png`) or generated at build time with `opengraph-image.tsx` using the `ImageResponse` API from `next/og`
  • 5Sitemaps and robots.txt are generated via `sitemap.ts` and `robots.ts` file conventions โ€” they export typed functions and are cached by default
  • 6JSON-LD structured data is rendered as a `<script type="application/ld+json">` tag inside your page component โ€” there is no special API, which keeps it flexible and explicit

Master next.js metadata & seo

Take the Next.js Full-Stack course with hands-on lessons and challenges.

Examples

Static metadata with title template in root layout

tsx

The root layout defines metadataBase (used to resolve relative OG image URLs), a title template that appends '| Acme' to every child page title, and default OpenGraph/Twitter card settings. Child pages only need to export their specific title and description โ€” everything else is inherited.

Dynamic generateMetadata for product pages

tsx

generateMetadata fetches product data and returns metadata specific to that product. The fetch is automatically memoized โ€” if the page component calls getProduct with the same slug, it will not make a second request. The canonical URL in alternates tells search engines the authoritative URL for this content.

Generated OG image with ImageResponse

tsx

Place an opengraph-image.tsx file inside any route segment and Next.js generates the OG image automatically. The ImageResponse API uses a Satori-based renderer that converts JSX to PNG โ€” no headless browser needed. The exported alt, size, and contentType are used for the meta tags. This is generated at build time for static routes or on-demand for dynamic ones.

Dynamic sitemap with database query

tsx

A single sitemap.ts file replaces XML sitemap plugins entirely. It queries the database at build time (or request time if using dynamic APIs), returns a typed array, and Next.js serializes it to valid XML at /sitemap.xml. For large sites with over 50,000 URLs, use generateSitemaps to split across multiple files.

Robots.ts with environment-aware rules

tsx

robots.ts uses environment variables to block all crawling in non-production environments โ€” a common mistake that leads to staging sites getting indexed. In production, it allows crawling of public pages while blocking API routes, authenticated areas, and AI crawlers. The sitemap reference ties it all together.

JSON-LD structured data for an article

tsx

JSON-LD is rendered as a script tag inside your component โ€” no special Next.js API needed. The schema-dts package provides full TypeScript types for Schema.org vocabularies. The replace call on angle brackets prevents XSS injection if any user-generated data ends up in the JSON. Google uses this structured data for rich results like article carousels and knowledge panels.

Common Mistakes

Mistake:

Setting `metadataBase` as a relative URL or forgetting it entirely โ€” OG image URLs resolve to localhost or broken paths in production

Fix:

Always set `metadataBase: new URL('https://yourdomain.com')` in your root layout. All relative image URLs in metadata (like `/og.png`) resolve against this base. Without it, social sharing previews will be broken.

Mistake:

Exporting both `metadata` and `generateMetadata` from the same file โ€” this causes a build error

Fix:

A route segment can only use one approach. Use the static `metadata` object for pages with fixed metadata. Use `generateMetadata` only when you need access to route params, search params, or fetched data.

Mistake:

Not blocking staging and preview environments from search engine indexing โ€” Google indexes your staging site and it competes with production

Fix:

Use `robots.ts` with an environment check. In non-production environments, return `{ rules: { userAgent: '*', disallow: ['/'] } }`. Alternatively, set `<meta name='robots' content='noindex'>` via metadata in non-production layouts.

Mistake:

Duplicating metadata between layout and page without using title templates โ€” leads to inconsistent titles and wasted effort

Fix:

Define `title: { template: '%s | Brand', default: 'Brand' }` in your root layout. Child pages only export `title: 'Page Name'` and the template appends the suffix automatically. This guarantees consistency and reduces boilerplate.

Best Practices

  • Set `metadataBase` in your root layout โ€” every relative URL in your metadata (OG images, canonical links) resolves against it, preventing broken social previews
  • Use `alternates.canonical` on every page to tell search engines the authoritative URL โ€” this consolidates ranking signals and prevents duplicate content issues from query parameters or trailing slashes
  • Prefer `generateMetadata` over client-side document.title manipulation โ€” server-rendered metadata is visible to crawlers on first response, while client-side changes may be missed
  • Use `opengraph-image.tsx` with `ImageResponse` for dynamic OG images instead of relying on external services โ€” it runs at build time for static pages, needs no API key, and keeps your image generation in version control
  • Add JSON-LD structured data for content-heavy pages (articles, products, courses, events) โ€” it directly enables Google rich results and is more reliable than relying on meta tags alone for semantic meaning
  • Generate sitemaps programmatically with `sitemap.ts` rather than maintaining static XML โ€” it stays in sync with your database and the TypeScript types catch structural errors at build time

Summary

Next.js turns SEO from a maintenance burden into a first-class part of your routing layer. Static metadata is a typed export, dynamic metadata is an async function that receives route params, and OG images, sitemaps, and robots.txt are all file conventions with TypeScript support. The key patterns are: set metadataBase and title templates in your root layout, use generateMetadata for dynamic routes, generate OG images with ImageResponse, add JSON-LD for rich results, and always include canonical URLs. Every piece of your SEO configuration lives alongside the routes it describes, is type-checked, and merges automatically across layouts.

Practice Next.js with hands-on challenges

Learn next.js metadata & seo hands-on in your IDE

Interactive lessons and challenges on Stanza, practice in VS Code, Cursor, or the web.

Related Concepts

Related Cheatsheets

Master Next.js with Stanza

Interactive lessons and challenges, right in your code editor.

Check the free courses. No credit card.