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.
Master next.js metadata & seo
Take the Next.js Full-Stack course with hands-on lessons and challenges.
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.
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.
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.
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 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 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.
Setting `metadataBase` as a relative URL or forgetting it entirely โ OG image URLs resolve to localhost or broken paths in production
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.
Exporting both `metadata` and `generateMetadata` from the same file โ this causes a build error
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.
Not blocking staging and preview environments from search engine indexing โ Google indexes your staging site and it competes with production
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.
Duplicating metadata between layout and page without using title templates โ leads to inconsistent titles and wasted effort
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.
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.
Interactive lessons and challenges on Stanza, practice in VS Code, Cursor, or the web.
Interactive lessons and challenges, right in your code editor.
Check the free courses. No credit card.