Next.js SEO: An App Router Playbook That Ranks
Most Next.js sites leave rankings on the table with a few avoidable mistakes. Here is the App Router SEO playbook we use to ship sites that Google can actually read.
Next.js gives you almost everything you need to rank well out of the box, and most teams still ship sites that Google struggles to read. The framework is not the problem. The problem is that SEO gets treated as a final checkbox instead of an architectural decision, so metadata ends up scattered, content renders on the client, and the structured data never gets written.
The App Router changed how all of this works. The generateMetadata function, file-based conventions for sitemap.ts and robots.ts, and Server Components as the default each remove a class of SEO bug that used to be common in the Pages Router. But they only help if you use them deliberately.
This is the playbook we follow when we build a Next.js site that has to rank, the same approach behind this site. It is opinionated and concrete: where to put metadata, which files to ship, how to handle structured data and multiple languages, and why Core Web Vitals is an SEO feature rather than a performance afterthought. None of it requires a plugin.
Render on the server so Google sees real HTML
The single biggest SEO win in Next.js is also the easiest to get wrong: make sure your indexable content is in the HTML on the first byte. Googlebot will execute JavaScript, but it does so on a delay and with no guarantees. Content that depends on a client-side fetch can be missed, indexed late, or indexed empty.
Server Components are the default in the App Router, so this is mostly about not opting out. Keep 'use client' at the leaves of your tree, on the button that needs an onClick, not on the page that holds your copy. Fetch your data in the Server Component and pass the rendered result down. If you can view the page source and read your headline and body text without JavaScript, you are in good shape.
Master the Metadata API instead of next/head
In the App Router you never touch next/head. Every route exports either a static metadata object or a dynamic generateMetadata function, and Next.js merges and deduplicates these across nested layouts for you.
Use the static export for fixed pages and the async function for anything driven by data, like a blog post or a product:
export async function generateMetadata({ params }): Promise<Metadata> {
const post = await getPost(params.slug);
return {
title: post.title,
description: post.description,
alternates: { canonical: `/blog/${post.slug}` },
openGraph: { title: post.title, type: "article" },
};
}Set a title.template in your root layout so every page gets a consistent suffix, always provide a canonical URL through alternates, and let opengraph-image.tsx generate social cards at the edge.
Ship the technical files: sitemap, robots, and RSS
The App Router turns three tedious SEO assets into ordinary code. Add app/sitemap.ts that exports a function returning your routes, add app/robots.ts to point crawlers at it, and you have a sitemap that stays in sync with your content because it is generated from the same data source your pages use.
Do not hand-maintain a static XML file. Generate the sitemap from your real list of pages and posts so a new article appears the moment it is published. If you publish content, expose an RSS feed too. It helps syndication and gives both readers and crawlers a clean, dated index of everything you have written.
Add structured data and hreflang
Structured data is how you earn rich results. Inject JSON-LD with a script tag in your Server Component: Article and BreadcrumbList for posts, Organization for your brand, Product or FAQPage where they genuinely apply. Keep the markup honest and matched to what is visible on the page, because mismatches get penalized rather than rewarded.
If you serve more than one language, the alternates.languages field in your metadata emits the hreflang tags that tell Google which version to show which audience. Pair that with one canonical URL per piece of content so you never split ranking signals across near-duplicate pages.
Treat Core Web Vitals as an SEO feature
Core Web Vitals are a ranking input, not just a performance scorecard. The three that matter are Largest Contentful Paint, Interactivity (INP), and Cumulative Layout Shift, and Next.js gives you the tools to win all three.
Use next/image so images are sized, lazy-loaded, and served in modern formats, which protects LCP and CLS. Use next/font to self-host fonts and kill layout shift from late font swaps. Lean on Server Components to ship less JavaScript, which is the most direct lever on INP. Then measure with real field data, not just a lab score, because the numbers Google uses come from real visitors.
A pre-launch SEO checklist
Before you ship, run through the basics: every page has a unique title and description, content is visible in view-source without JavaScript, the sitemap and robots files resolve, canonical URLs are set, structured data validates, and your Core Web Vitals are green on mobile. None of these take long on their own. Skipping them is what quietly costs sites months of rankings.