The single most important SEO decision in Next.js is how each route renders. Google can execute JavaScript, but rendering is queued, delayed, and not guaranteed — and most other crawlers (Bing partially, and almost all AI crawlers) index only the initial HTML response.
The rule of thumb:
- Static (SSG) — marketing pages, docs, blog posts. Fastest, cheapest, fully indexable.
- ISR (Incremental Static Regeneration) — catalogs, listings, anything that changes hourly/daily. Static speed with controlled freshness.
- SSR (dynamic rendering) — personalized or truly real-time pages. Indexable, but slower TTFB.
- CSR (client-only) — dashboards and app internals. Never for content you want indexed.
// app/blog/[slug]/page.tsx — ISR: static HTML, regenerated hourly
export const revalidate = 3600;
export async function generateStaticParams() {
const posts = await getPosts();
return posts.map((post) => ({ slug: post.slug }));
}
Check what the crawler receives — not what your browser shows:
curl -s https://example.com/your-page | grep -i "your-key-phrase"
If your key content isn't in that response, it doesn't exist for most crawlers.
Watch out for accidental dynamic rendering. Using cookies(), headers() or searchParams in a page silently switches it to SSR. That's not fatal for SEO, but it costs TTFB and server load. We covered these traps in detail in How Caching Works in Next.js with App Router.
Checklist:
- Every indexable route is SSG or ISR; SSR only where personalization demands it
-
curlthe raw HTML and confirm the main content is there - No
cookies()/headers()calls on pages that should be static - Client components used for interactivity, not for content