Guides
Next.js SEO Checklist: Metadata, Rendering and Routing
Next.js does most of SEO correctly by default. The failures come from choosing the wrong rendering mode for a route and from metadata that runs on the client.
Lancez un audit DomainLens frais et utilisez le rapport comme liste de priorités.
Rendering mode decides everything else
Next.js can render a route statically at build time, on the server per request, or entirely on the client. The first two produce complete HTML and behave like any server-rendered site. The third produces an empty shell and inherits every risk of client-side rendering.
The practical rule: any route that should rank must render on the server or be statically generated. Client-only rendering is for authenticated dashboards, not for public content.
| Mode | When to use | SEO risk |
|---|---|---|
| Static (SSG / ISR) | Content that changes rarely | None |
| Server (SSR) | Per-request or personalised content | None |
| Client only | Authenticated app routes | High — empty first pass |
| Partial prerender | Mixed static shell, dynamic parts | Low, if the shell has content |
Metadata must render on the server
The App Router's metadata export and generateMetadata function both run server-side, which is what makes them safe. Setting titles or canonical tags from a client component means they exist only after hydration, which is too late for the first indexing pass.
This is the most common Next.js SEO defect, and it survives review because the tags are unquestionably present when you inspect the page in a browser.
// app/blog/[slug]/page.tsx
export async function generateMetadata({ params }) {
const post = await getPost(params.slug);
return {
title: post.title,
description: post.excerpt,
alternates: { canonical: `https://example.com/blog/${params.slug}` },
openGraph: { title: post.title, images: [post.image] },
};
}
// Pages Router equivalent uses next/head inside the page
// component — also server-rendered, and also fine.
The checklist
The canonical point catches people out: Next.js handles almost everything and does not emit a canonical tag on its own. A site can be perfectly rendered and still have every parameter variant competing.
- 1Confirm every public route renders on the server — fetch it with curl and read the HTML.
- 2Set metadata through the metadata export or generateMetadata, never from a client component.
- 3Declare a canonical for every page; Next.js does not add one automatically.
- 4Use the not-found convention so unmatched routes return a real 404 rather than a 200 shell.
- 5Generate sitemap.ts and robots.ts from your content rather than maintaining static files.
- 6Use next/link for internal navigation — it renders a real anchor with an href.
- 7Use next/image with explicit dimensions, and set priority on the LCP image so it is not lazy-loaded.
The 404 that is not a 404
A catch-all route rendering a not-found component returns 200 unless the framework convention is used. That produces soft 404s at every typo and stale link, and on a large site the count is unbounded. The App Router's not-found file and the notFound() helper set the status correctly; a hand-rolled fallback usually does not. Verify with curl rather than by looking at the page. See soft 404 errors .
Performance defaults worth keeping
The priority flag on the hero image is the single most common Next.js performance mistake, because next/image lazy-loads by default and that default is wrong for exactly one image on each page. See lazy loading images .
- next/image handles sizing, format negotiation and lazy loading — but set priority on the hero, or it lazy-loads your LCP element.
- next/font removes the render-blocking font request, which is a free CLS and LCP improvement.
- Static generation with ISR gives you cached HTML with periodic freshness, which fixes TTFB almost entirely.
- Route-level code splitting is automatic; adding manual dynamic imports on top often makes interaction worse.
How DomainLens contributes
DomainLens reads the served HTML, so a route that renders on the client or a metadata export that quietly moved to a client component shows up as missing tags rather than as a mystery. See JavaScript SEO for how rendering works and the React checklist for the framework underneath.
- Is Next.js good for SEO?
- Yes, when routes render on the server or are statically generated. Client-only rendering in Next.js carries the same risks as any SPA.
- Does Next.js add canonical tags automatically?
- No. Set them through the alternates field in your metadata, or every URL variant competes with itself.
- Why does my Next.js 404 page return 200?
- A component rendering a not-found message does not set the status. Use the not-found convention or call notFound() so the server sends 404.
- Should I use next/image?
- Yes, with explicit dimensions. Set priority on the LCP image, since the default lazy-loading behaviour is wrong for it.