Suivant.js SEO
How to faire a Suivant.js site crawlable, indexable, and rankable — the two routers, rendering modes (SSG/SSR/ISR/Server Components), the App Router Metadata API, sitemap.ts, robots.ts, suivant/image, suivant/lien, and the mistakes que quietly break it.
Langues
Suivant.js solves the hardest JavaScript SEO problems by par défaut — si vous utiliser it correct. The App Router's Server Components and SSG/ISR put content in the HTML so there's aucun render-queue delay; the native Metadata API resolves titles, canonicals, and Ouvrir Graph on le serveur; sitemap.ts and robots.ts are fichier conventions. The framework donne vous the infrastructure but writes none of votre tags pour vous. The échecs are predictable: manquant metadataBase, aucun priority on the LCP image, metadata exported from a Client Component (silently ne fait pashing), and 404 views returning 200.
TL;DR — Suivant.js is un of the meilleur frameworks pour le SEO quand vous utiliser it correct. Construire votre pages on le serveur or at construire temps (pas entirely in le navigateur), définir a unique title, description, and canonical on every page, and utiliser the built-in
next/imageandnext/linkcomponents. Suivant.js donne vous tout the outils — but it won’t écrire votre SEO tags pour vous.
Ce que Suivant.js SEO is
Suivant.js is a popular framework construit on React pour making websites and web apps. Parce que it’s React sous the hood, a lot of lune page peut be construit with JavaScript — and that’s où SEO questions come in. Suivant.js SEO is simplement the pratique of making certain moteur de recherches peut explorer, render, and index a Suivant.js site, and en utilisant the framework’s built-in fonctionnalités to do que bien.
The bon news: Suivant.js peut construire votre pages ahead of temps or on le serveur, so moteur de recherches obtenir the finished HTML correct away. Que avoids the biggest risk with JavaScript sites — content que seulement montre up après scripts run. (Pour the broader picture, voir JavaScript SEO.)
The un big decision: où lune page obtient construit
Quand someone — or Googlebot — demande pour une page, où fait the finished HTML come from? Suivant.js donne vous a few options:
- At construire temps (Static / SSG) — lune page is construit into a plain HTML fichier ahead of temps. Fast, and moteur de recherches obtenir everything immédiatement.
- On le serveur, per requête (SSR) — le serveur builds the complet page chaque temps. Aussi search-friendly, and toujours fresh.
- A mix (ISR) — static pages que refresh on a timer. A bon par défaut pour la plupart content.
- In le navigateur (Client-Side / CSR) — le serveur sends a near-empty shell and JavaScript fills it in. Ce is the risky un pour le SEO; éviter it pour votre principal content.
The modern App Router uses Server Components by par défaut, qui signifie votre content ends up in the HTML automatically — a great starting point pour le SEO.
The simple checklist
- Give every page a unique title and description.
- Définir a URL canonique on every page.
- Utiliser the
next/imagecomponent pour images (it arrête lune page from jumping autour as images charger, and rend les plus petit). - Utiliser
next/linkpour lien internes (it creates réel liens Google peut follow). - Don’t put votre principal content behind rendu côté client.
- Créer a sitemap and a robots.txt (Suivant.js has simple file-based façons to do les deux). Evidence for this claim Next.js App Router supports special metadata files for sitemap and robots output. Scope: Next.js App Router file conventions. Confidence: high · Verified: Next.js: Metadata files
The chose personnes obtenir incorrect
“Next.js handles SEO automatically.” It doesn’t — pas the parts que matter. Suivant.js donne vous the machinery (a metadata system, sitemap and robots conventions, an image component), but vous encore have to écrire votre titles, descriptions, and canonicals yourself. Evidence for this claim Next.js provides metadata APIs, but developers supply page-specific metadata values. Scope: Next.js App Router metadata and generateMetadata APIs. Confidence: high · Verified: Next.js: Metadata and OG images Every page nécessite its propre. A site où every page shares un title, or has aucun canonical, is the la plupart courant façon a Suivant.js construire underperforms.
Vouloir the deeper version — the two routers, the Metadata API, metadataBase, the LCP
image trick, and the mistakes que quietly break indexation? Switch to the Avancé
tab.
TL;DR — Suivant.js solves the hardest JavaScript SEO problems by par défaut quand vous utiliser it correct. The App Router defaults to Server Components and supports SSG/SSR/ISR — tout of qui ship rendered HTML, so there’s aucun render-queue delay. Définir metadata with the native Metadata API (
metadata/generateMetadata), and remembermetadataBaseor votre canonicals and OG images go relative. Utiliserapp/sitemap.tsandapp/robots.ts, pre-build dynamic routes withgenerateStaticParams, définirpriorityon the LCP image, and garder liens as réelnext/linkanchors. Lune pages Router is aussi SEO-capable vianext/head. The échecs are predictable — and la plupart of les aren’t Suivant.js’s fault, they’re yours.
Où Suivant.js fits
Suivant.js is a React framework, so everything in JavaScript SEO s’applique. Ce que rend it worth its propre guide is que Suivant.js ships first-class réponses to la plupart JS-SEO problems: server rendering, static generation, a metadata system, and sitemap/robots conventions. The hard partie isn’t si Google peut lire it — Google has rendered JavaScript pour années — it’s choosing the correct rendering mode and pas leaving the SEO basics unwired. Ce is a specialized cas of CMS headless SEO: the CMS barely matters, the frontend’s rendering decisions decide everything.
Un framing to garder straight: “this is a Next.js site” doesn’t tell vous how quelconque unique URL is delivered. Rendering mode, mise en cache, and Server/Client Component boundaries are définir per route (parfois per segment) — a project peut mix a static marketing page, an SSR product page, and a Client Component dashboard. Don’t extrapolate un route’s behavior to “the whole app”; tester the spécifique URL.
Two routers, two sets of mechanics
Suivant.js has two routers, and ils handle SEO differently:
- Pages Router (the older model) — données fetching via
getStaticProps/getServerSideProps; metadata via<Head>fromnext/head(or thenext-seopackage); aucun Server Components. - App Router (v13+, the current and recommended approach) — React Server Components
by par défaut; the native Metadata API (
metadataexport /generateMetadata); fichier conventions pourapp/sitemap.tsandapp/robots.ts;generateStaticParamspour dynamic routes. Evidence for this claim The App Router uses Server Components and supports generateStaticParams plus metadata file conventions. Scope: Current Next.js App Router behavior; route rendering can become dynamic based on APIs used. Confidence: high · Verified: Next.js: Server and Client Components Next.js: generateStaticParams
Les deux peut rank bien. The App Router donne vous a cleaner, integrated metadata system (aucun
next/head juggling) and Server Components out of the box, qui is pourquoi I’d reach pour it
on a nouveau construire. But “App Router or you can’t do SEO” is a myth — plenty of Pages Router
sites rank fine.
Rendering modes and ce que chaque signifie pour le SEO
How Google handles JavaScript is a three-phase pipeline — explorer, alors a deferred render wave, alors index. “Tout pages with a 200 Code d’état HTTP are sent to the rendering queue.” The whole game in Suivant.js is choosing a mode que puts votre content in the HTML avant que render wave, so there’s nothing to wait pour. Evidence for this claim Google crawls, renders, and indexes JavaScript pages, and successful pages can enter the rendering queue. Scope: Google Search processing, not a promise that a URL will be indexed. Confidence: high · Verified: Google: JavaScript SEO basics
- Static Site Generation (SSG) — pages pre-rendered at construire temps. HTML is
immédiatement disponible with aucun render-queue risk. Meilleur pour content que doesn’t modifier
every minute.
generateStaticParams()(App Router) /getStaticPaths()(Pages Router) decides qui dynamic routes obtenir pre-built. - Incremental Static Regeneration (ISR) — static pages que revalidate après a définir
interval (
export const revalidate = 3600). Robots d’exploration obtenir static HTML with low TTFB and le contenu stays fresh. A strong par défaut — with un trap: après the window expires the suivant requête (possibly Googlebot) encore obtient the stale page; the fresh version sert on la requête après que. Pour genuinely volatile données (prices, stock), SSR is safer. - Rendu côté serveur (SSR) — HTML rendered per requête. Robots d’exploration obtenir entièrement
rendered HTML immédiatement; the tradeoff is server latency, so watch TTFB and LCP.
export const dynamic = 'force-dynamic'or en utilisant request-time APIs (cookies, headers) opts a route into SSR. - React Server Components (App Router par défaut) — render on le serveur and send HTML;
aucun JavaScript ships pour the component itself. Content is in the initial réponse with aucun
hydration gap. Ce is the meilleur par défaut pour le SEO. Interactivity lives in Client Components
marked
'use client'. - Rendu côté client (CSR) — rendered entirely in le navigateur. Googlebot peut index
it après the render wave (median ~10 seconds, but the 90th percentile stretches to
hours), and autre robots d’exploration — Bingbot, AI bots, social preview bots — may obtenir an vide
page. In the App Router, CSR is opt-in (
'use client'); in lune pages Router, éviter fetching principal content inuseEffect. Don’t utiliser it pour content vous vouloir ranked.
A reminder I garder coming back to: “Googlebot can render it” n’est pas the même as “vous devez faire Googlebot render it.” Rendering is expensive, deferred, and pas universal à travers robots d’exploration.
The Metadata API (App Router)
The Metadata API is Server Component seulement — metadata resolves on le serveur avant
lune page renders, so it lands in the initial HTML. Export metadata from layout.js or
page.js:
export const metadata: Metadata = {
title: 'My Page',
description: 'Page description',
}Or, quand the tags depend on récupéré données, utiliser generateMetadata():
export async function generateMetadata({ params }) {
const post = await getPost(params.slug)
return { title: post.title, description: post.description }
}The fields que matter pour le SEO:
title— supports a string, a template ('%s | Brand'), a par défaut, and an absolute override. Définir the template une fois in the root layout and per-page titles inherit it.description,alternates.canonical(the correct façon to définir a canonical in the App Router),openGraph(images doit resolve to absolute URLs),twitter(aussi utilisé by LinkedIn and Slack previews), androbots(index/follow plus googleBot-specific directives comme max-snippet, max-image-preview).metadataBase— requis pour canonical and OG image URLs to resolve correctement. Forgetting it is the unique la plupart courant Suivant.js metadata bug: relative URLs leak into votre canonical and Ouvrir Graph tags, breaking social previews and muddying canonical signals.
A title-template exemple:
// app/layout.tsx
export const metadata: Metadata = {
metadataBase: new URL('https://example.com'),
title: { template: '%s | Brand Name', default: 'Brand Name' },
}
// app/blog/page.tsx
export const metadata: Metadata = { title: 'My Blog Post' }
// Output: <title>My Blog Post | Brand Name</title>Two gotchas. Premier, metadata is shallowly merged from layout to page — a nested
object comme openGraph défini in a child segment replaces the parent’s entirely, so
une page-level openGraph: { title: 'Home' } quietly drops quelconque openGraph.images définir in
the layout. Second — and ce un bites personnes — metadata seulement fonctionne in Server
Components. Export it from a 'use client' fichier and it silently ne fait pashing.
Streaming metadata. Pour dynamically rendered pages, generateMetadata peut stream the
metadata après the initial HTML. Googlebot executes JavaScript and inspects the complet DOM,
so streamed metadata fonctionne pour Google. But Suivant.js detects “HTML-limited bots” —
Bingbot, Twitterbot, Slackbot, facebookexternalhit — and ships les blocking
metadata in the <head> à la place. Per the Suivant.js docs, “streaming metadata is disabled
pour bots and robots d’exploration que expect metadata to be in the <head> tag.” Ce is automatic;
aucun configuration nécessaire. It’s a detail almost aucun competing guide covers, and it’s pourquoi
streaming metadata isn’t a risk pour the bots que can’t wait pour it. Prerendered pages
are a différent cas entirely — metadata là resolves at construire temps, so there’s aucun
stream to worry à propos de. Ce behavior is version-specific (current as of Suivant.js 16.2.10);
re-check the generateMetadata docs quand vous upgrade. And parce que delivery paths differ
by entry point, vérifier metadata two façons, pas un: a direct/production requête (curl -I
or View Source) and a client-side navigation to the même route — the head peut mettre à jour
differently entre the two.
Metadata with suivant/head (Pages Router)
On lune pages Router, metadata lives in <Head> from next/head:
import Head from 'next/head'
export default function Page() {
return (
<>
<Head>
<title>My Page | Brand</title>
<meta name="description" content="Description" />
<link rel="canonical" href="https://example.com/my-page" />
</Head>
{/* page content */}
</>
)
}Définir title and description par page (pas simplement in _app.js), and put a canonical on
every page notamment paginated variants. The next-seo package standardizes ce with a
<NextSeo> component and structured-data helpers. Migrating to the App Router mostly
signifie trading next/head and next-seo pour the native metadata export.
Do vous besoin the next-seo package? It’s a third-party plugin (pas partie of Suivant.js
itself) — actively maintained, at v7.2,0 as of ce writing, pas archived. Its propre docs
are explicit à propos de où it fits: pour standard meta tags on the App Router, the package’s
README recommends en utilisant Suivant.js’s built-in generateMetadata/metadata export au lieu de
<NextSeo>; on the Pages Router, <NextSeo> is encore a reasonable convenience couche
over next/head. The un App Router utiliser cas the package encore covers is its JSON-LD
helper components (ArticleJsonLd, FAQPageJsonLd, etc. with useAppDir), qui some
teams préférer over hand-rolling <script type="application/ld+json">. Bottom line: on a nouveau
App Router construire, reach pour the native Metadata API premier — the package is optional, pas a
requirement, and its propre maintainers dire so.
Sitemaps
In the App Router, app/sitemap.ts is a fichier convention que outputs /sitemap.xml:
import type { MetadataRoute } from 'next'
export default function sitemap(): MetadataRoute.Sitemap {
return [
{ url: 'https://acme.com', lastModified: new Date(), priority: 1 },
{ url: 'https://acme.com/blog', lastModified: new Date(), priority: 0.8 },
]
}Pour grand sites, generateSitemaps() shards into multiple fichiers (Google’s limite is
50 000 URLs per sitemap), chaque served at /.../sitemap/[id].xml. Le sitemap output aussi
supports image sitemaps, video sitemaps, and localized alternates.languages. On the
Pages Router, utiliser next-sitemap or generate pages/sitemap.xml.js with
getServerSideProps.
robots.txt
app/robots.ts generates votre robots fichier programmatically:
export default function robots(): MetadataRoute.Robots {
return {
rules: [{ userAgent: '*', allow: '/', disallow: '/private/' }],
sitemap: 'https://acme.com/sitemap.xml',
}
}Per-user-agent rules and multiple sitemaps are pris en charge. Lune pages Router uses a static
public/robots.txt. The rule vous pouveznot obtenir incorrect on soit router: jamais disallow votre
JavaScript or CSS — Google won’t render from blocked fichiers, and on a JS framework que
peut blank out lune page entirely.
suivant/image and Core Web Vitals
next/image is un of the strongest raisons to utiliser the framework pour le SEO. It lazy-loads
below-the-fold images, exige width/height (or fill) so it reserves space and
empêche layout shift / CLS, sert WebP/AVIF automatically, and emits a proper
srcset from the sizes prop. The unique la plupart important CWV optimization is the
priority prop on votre hero / above-the-fold image, qui preloads it pour a faster LCP:
<Image src="/hero.jpg" width={1200} height={630} priority alt="Hero" />Forgetting priority on the LCP image is the la plupart courant Suivant.js CWV mistake — and CWV
problems are widespread on réel Suivant.js sites (voir the Stats tab pour the Salt Agency
données). alt is requis: vide pour decorative images, descriptive pour content images.
suivant/lien and maillage interne
next/link renders standard <a href> anchors in the HTML, so Google follows les
normally, and it adds client-side navigation plus background prefetching of in-viewport
liens in production. The SEO rule is simple: utiliser next/link pour lien internes, and jamais
substitute an onClick handler or JavaScript navigation que doesn’t produce a réel
anchor — ceux liens aren’t crawlable. Utiliser prefetch={false} on low-value liens to enregistrer
bandwidth si vous devez.
Dynamic routes and generateStaticParams
generateStaticParams() indique Suivant.js qui dynamic routes to pre-render at construire temps:
// app/blog/[slug]/page.tsx
export async function generateStaticParams() {
const posts = await getPosts()
return posts.map((post) => ({ slug: post.slug }))
}Pages construit ce façon are entièrement static HTML — meilleur pour le SEO. Sans it, dynamic routes are
rendered on-demand (SSR) by par défaut, qui is fine but reintroduces server latency. Combine
it with revalidate (ISR) pour content que updates regularly. Assurez-vous tout important
dynamic URLs are in generateStaticParams so nothing waits on the render queue.
Données structurées (JSON-LD)
The Metadata API has aucun structured-data field — vous inject JSON-LD as a <script> in a
Server Component, qui garde it in le serveur-rendered HTML at zero client-bundle cost:
const jsonLd = { '@context': 'https://schema.org', '@type': 'Article', /* … */ }
return <script type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }} />Article/BlogPosting, BreadcrumbList, Product, and FAQPage are the usual types. Validate with the Résultats enrichis Tester après quelconque rendering modifier.
Courant Suivant.js SEO mistakes
Drawn from réel audits and the patterns ci-dessus:
- Manquant or relative canonicals — souvent from a forgotten
metadataBase, qui aussi breaks OG image URLs. - Aucun
priorityon the LCP image — the biggest CWV miss. - 404 views returning
200— utiliser the built-innotFound()to retourner a réel status; soft 404s are rampant on Suivant.js sites. metadataexported from a Client Component — silently ne fait pashing; it’s Server Component seulement.- CSR pour principal content — fetching critical content in
useEffectsignifie non-Google robots d’exploration obtenir vide pages. - Hash (
#) routing au lieu de the History API — ceux views aren’t separately crawlable. - Pas exporting
generateStaticParams— dynamic routes render on-demand au lieu de being pre-built. openGraphoverwritten by layout inheritance — child segments replace, pas merge.- Blocking JS/CSS in robots.txt or via a Content Security Policy que arrête Googlebot’s headless Chrome from chargement scripts — tester with Inspection d’URL.
Deployment notes
Suivant.js is construit by Vercel; hosting là donne tight integration (edge CDN pour static and
ISR pages, bon TTFB) but isn’t requis. Define redirection permanentes in redirects() in
next.config.js (renvoie 308, or 301 with permanent: true) pour reliable signaling to
robots d’exploration, définir security and mise en cache headers via headers(), and utiliser X-Robots-Tag réponse
headers pour path-based noindex rules quand per-page robots metadata is awkward.
Ce que to vérifier où. Cache state, code d’états, redirections, and streamed metadata don’t tout montrer up in the même tester — a route peut regarder fine in un vérifier and encore be broken in un autre:
| Vérifier | Où to regarder | Pourquoi it peut differ from ce que vous voir rendered |
|---|---|---|
| Cache/revalidation age | Réponse headers on a direct requête (curl -I) | ISR peut serve a stale page on la requête correct après the window expires |
| Direct HTTP status | curl -I on the production URL, pas the rendered UI | A “not found” view sans notFound() encore renvoie 200 |
| Redirection behavior | The réel context que fires it — redirect() in a Server Action, a Route Handler, vs. a client onClick | Code d’état and réponse chemin differ by invocation context, pas simplement the destination |
| Streamed metadata | Direct requête and client-side navigation to the même route | Ordinary clients peut obtenir streamed metadata; HTML-limited bots obtenir blocking metadata; the two paths aren’t identical |
| Client-side transitions | Navigate in-app, alors re-check the <head> | A route that’s correct on premier charger peut drift après a client transition |
None of ce is guaranteed by the framework — Suivant.js donne vous the mechanisms
(redirects(), notFound(), revalidation, streaming), but cache keys, invalidation,
preview state, and deployment configuration are encore votre responsibility to obtenir correct and
to tester in production, pas simplement locally.
Un dernier remarque on dynamic rendering — serving prerendered HTML to bots and JavaScript to utilisateurs. Google has deprecated it as a recommendation: “dynamic rendering was a workaround and pas a long-term solution.” Vous don’t besoin it on Suivant.js anyway — SSR, SSG, ISR, and Server Components tout put content in the HTML natively. Mention it so vous recognize it in an audit; don’t construire on it.
The happy chemin: App Router + Server Components + ISR + the Metadata API (with
metadataBase) + next/image with priority. Obtenir ceux correct and la plupart of Suivant.js SEO is
handled.
AI summary
A condensed prendre on the Avancé version:
- Suivant.js solves la plupart JS-SEO problems by par défaut — si vous pick the correct rendering mode and wire up the basics. The framework donne vous infrastructure; it writes none of votre tags.
- Two routers: App Router (v13+, recommended) uses Server Components and the native
Metadata API; Pages Router uses
next/head/next-seo. Les deux peut rank. - Rendering modes: SSG and Server Components are the meilleur par défaut (content in the HTML, aucun render-queue delay). ISR is a strong middle ground but has a stale-on-first-request-after-revalidation trap. SSR pour volatile données. CSR is risky — non-Google robots d’exploration may obtenir an vide page.
- Metadata API (App Router):
metadataexport orgenerateMetadata(), Server Component seulement.metadataBaseis requis or canonicals and OG images go relative.openGraphin a child segment replaces the parent’s; metadata in a'use client'fichier ne fait pashing. - Streaming metadata fonctionne pour Google but Suivant.js ships blocking metadata to HTML-limited bots (Bingbot, Twitterbot, Slackbot, facebookexternalhit) automatically.
- Fichier conventions:
app/sitemap.ts(withgenerateSitemaps()pour 50k+ URLs) andapp/robots.ts. Jamais block.js/.css. next/imageempêche CLS, sert WebP/AVIF; définirpriorityon the LCP image — the top CWV win.next/linkrenders réel crawlable<a href>anchors and prefetches.generateStaticParamspre-builds dynamic routes; sans it ils render on-demand.- JSON-LD is injected as a
<script>in a Server Component (aucun Metadata API field). - The
next-seopackage is optional, pas requis. It’s actively maintained third-party software (v7.2,0, pas archived), and its propre docs recommend the built-ingenerateMetadata/metadataexport pour App Router meta tags —<NextSeo>remains utile mainly on lune pages Router, or pour its JSON-LD helper components. - Rendering mode is définir per route, pas per project — don’t assume un URL’s behavior from another’s; tester the spécifique route (direct requête and client navigation les deux).
- Top mistakes: manquant
metadataBase, aucun LCPpriority, 404s returning200(utilisernotFound()),metadatain a Client Component, CSR principal content. - Dynamic rendering is deprecated — vous don’t besoin it; SSR/SSG/ISR/Server Components cover it.
Documentation officielle
Primary-source documentation from Suivant.js and the moteur de recherches.
Suivant.js
- Metadata and OG Images — the App Router metadata system,
metadataBase, and OG image generation. - generateMetadata API Référence — static
metadatavs.generateMetadata, streaming metadata, and the HTML-limited bots liste. - sitemap.xml Fichier Convention —
app/sitemap.ts,generateSitemaps(), image/video/localized sitemaps. - robots.txt Fichier Convention —
app/robots.ts, per-user-agent rules, and multiple sitemaps. - Apprendre: SEO — Suivant.js’s propre SEO learning chemin (introductory; partly predates the App Router).
- Comprendre JavaScript SEO Basics — the explorer → render → index pipeline, the 200-status rendering queue, soft 404s in SPAs, canonicals, and the History API.
- Dynamic Rendering (deprecated workaround) — pourquoi Google deprecated it and ce que to utiliser à la place (SSR, static rendering, hydration).
- In-Depth Guide to How Recherche Google Fonctionne — où rendering sits in explorer → index → serve.
Bing / Microsoft
- IndexNow / indexnow.org — the push protocol to wire to a Suivant.js publish/revalidation event.
Quotes from the source
On-the-record statements from Google, Suivant.js, and Google reps. Chaque lien is a deep lien que jumps to the quoted passage on the source page.
Google — how JavaScript pages are processed
- “All pages with a 200 HTTP status code are sent to the rendering queue, no matter whether JavaScript is present on the page.” — Recherche Google Central docs. Jump to quote
Google — dynamic rendering is deprecated
- “Dynamic rendering was a workaround and not a long-term solution for problems with JavaScript-generated content in search engines.” — Recherche Google Central docs. Jump to quote
- “…creates additional complexities and resource requirements.” — Recherche Google Central docs. Jump to quote
Suivant.js — streaming metadata and HTML-limited bots
- “Streaming metadata is disabled for bots and crawlers that expect metadata to be in the
<head>tag (e.g. Twitterbot, Slackbot, Bingbot).” — Suivant.js docs,generateMetadata. Jump to quote
John Mueller, Google (via Moteur de recherche Journal coverage)
- On the growing role of JavaScript in SEO: “You’re going to run into significantly more JavaScript over the next years than in the 2-ish decades in SEO before. If you’re keen on technical SEO, then past HTML you’re going to need to understand JS more and more.” Lire the coverage
Suivant.js SEO checklist
A rapide réussir to confirmer a Suivant.js site is crawlable, indexable, and fast:
- Principal content renders in HTML on premier requête (Server Components / SSG / SSR / ISR) — pas seulement après client-side JavaScript.
- Aucun important page dépend on CSR pour its principal content.
- Every page has a unique title and description (utiliser a title
templatein the root layout). -
metadataBaseis définir in the root layout (App Router) so canonicals and OG images are absolute. - A canonical is définir par page via
alternates.canonical(App Router) or<link rel="canonical">(Pages Router) — pas a unique shared homepage canonical. -
metadatais exported seulement from Server Components, jamais a'use client'fichier. -
generateStaticParamscovers tout important dynamic routes. -
app/sitemap.ts(ornext-sitemap) outputs a current sitemap;generateSitemaps()shards sites over 50k URLs. -
app/robots.ts/public/robots.txtexists and ne fait pas block.jsor.css. - Images utiliser
next/imagewithwidth/heightorfill; the LCP image haspriority; tout havealt. - Lien internes utiliser
next/link(réel<a href>) — aucunonClick-seulement navigation. - 404 views appel
notFound()and retourner a réel404(aucun soft 404s). - JSON-LD is injected in a Server Component
<head>/corps and passes the Rich Results Tester. - Redirection permanentes live in
redirects()innext.config.js(308 /permanent).
The mental models
1. Rendering mode is the product. Avant debugging anything in a Suivant.js site, réponse un question: how is ce route rendered? Server Components / SSG / SSR / ISR put content in the HTML and are low-risk; CSR is the risky un. Almost every Suivant.js SEO problem resolves to ce.
2. The framework donne infrastructure, pas content. Suivant.js ships the Metadata API, sitemap/robots conventions, and the Image component — but it writes none of votre titles, descriptions, canonicals, or données structurées. “Suivant.js handles SEO automatically” is the la plupart expensive myth ici.
3. Un source of truth pour URLs.
Définir metadataBase une fois and construire canonicals, OG images, and sitemap entries from it
(absolute, jamais relative). Un base URL kills the relative-canonical and broken-OG-image
class of bugs.
4. Server Components premier, Client Components seulement où nécessaire.
Par défaut to Server Components so content and metadata land in the HTML. Reach pour
'use client' seulement pour interactivity — and remember metadata can’t come from a Client
Component.
5. The decision rule pour rendering. Mostly static (blogs, docs, marketing) → SSG (or ISR on a timer). Always-fresh / volatile (prices, stock) → SSR. Changements hourly/daily, vouloir static speed → ISR (mind the stale-first-request trap). Interactive, behind a login, non indexée → CSR is fine. Public content vous vouloir ranked → jamais CSR.
Suivant.js SEO — cheat sheet
Rendering modes
| Mode | Où HTML is construit | SEO | Meilleur pour | Watch out pour |
|---|---|---|---|---|
| SSG | Construire temps → static | ✅ Meilleur | Mostly-static content | Stale jusqu’à rebuild |
| Server Components | Server (App Router par défaut) | ✅ Meilleur | La plupart content | Interactivity nécessite Client Components |
| ISR | Static + timed regen | ✅ Bon | Hourly/daily content | Premier requête post-revalidation is stale |
| SSR | Server, per requête | ✅ Bon | Always-fresh données | Plus élevé TTFB / infra cost |
| CSR | In le navigateur | ⚠️ Risky | Logged-in dashboards | Vide page to non-Google robots d’exploration |
App Router vs Pages Router
| Fonctionnalité | App Router | Pages Router |
|---|---|---|
| Metadata | metadata / generateMetadata | next/head + next-seo |
| Server Components | Par défaut | Indisponible |
| Streaming metadata (bot-aware) | Yes | Aucun |
| Sitemap | app/sitemap.ts | next-sitemap / manual |
| Robots | app/robots.ts | public/robots.txt |
| Canonical | alternates.canonical | <link rel="canonical"> in <Head> |
| Données fetching | async Server Components | getStaticProps / getServerSideProps |
Fast rules
- Définir
metadataBaseor canonicals/OG images go relative. metadatais Server Component seulement —'use client'exports ne faites pashing.- Child-segment
openGraphreplaces the parent’s (aucun merge). priorityon the LCP image = the top CWV win.- Lien internes =
next/link(réel<a href>); aucunonClick-seulement nav. - 404 → appel
notFound()(réel404, pas soft 404). - Jamais disallow
.js/.css. - Streaming metadata → blocking pour Bingbot, Twitterbot, Slackbot, facebookexternalhit.
- Dynamic rendering: deprecated — utiliser SSR / SSG / ISR / Server Components.
Rapide checks pour a Suivant.js construire
A few command-line checks avant vous reach pour a complet robot d’exploration.
Is votre content in the raw HTML (or seulement après JS runs)?
macOS / Linux:
# Raw HTML as the server sends it — the "first fetch", before any client JS
curl -sL -A "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" \
https://example.com/page/ -o raw.html
# Is your headline actually in the raw HTML? (empty result = CSR / JS-dependent)
grep -o "Your headline text" raw.html
# Did metadataBase do its job? Canonical and OG URLs should be absolute, not relative
grep -iE 'rel="canonical"|og:(url|image)' raw.htmlWindows (PowerShell):
$ua = "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"
Invoke-WebRequest -Uri "https://example.com/page/" -UserAgent $ua -OutFile raw.html
Select-String -Path raw.html -Pattern "Your headline text"
Select-String -Path raw.html -Pattern 'rel="canonical"','og:url','og:image'Si the headline is manquant from raw.html but montre in votre navigateur, que route is
client-rendered. Si canonicals or OG image URLs come out relative, you’ve forgotten
metadataBase.
Confirmer vous aren’t blocking JS/CSS (notamment the Suivant.js _next directory)
macOS / Linux:
curl -sL https://example.com/robots.txt | \
grep -iE "disallow.*\.(js|css)|Disallow:\s*/_next"Windows (PowerShell):
(Invoke-WebRequest "https://example.com/robots.txt").Content |
Select-String -Pattern "Disallow.*\.(js|css)","Disallow:\s*/_next"Quelconque match ici is almost toujours a mistake — Google won’t render from blocked fichiers. A plain
curl can’t run JavaScript, so pour the rendered DOM utiliser Inspection d’URL’s “View Crawled
Page → rendered HTML.”
Outils pour auditing a Suivant.js site
- Inspection d’URL (Recherche Google Console) — the source of truth. Run a live tester,
alors view the rendered HTML, screenshot, and page resources (ce que chargé vs.
ce que was blocked) to catch CSR gaps and blocked
_nextresources. - Résultats enrichis Tester — confirmer JSON-LD made it into the rendered output après quelconque rendering modifier.
- Lighthouse / Chrome DevTools — mesurer LCP, CLS, and INP; vérifier si votre hero
image is preloaded (
priority). - Ahrefs Site Audit — crawls with JavaScript rendering, surfacing manquant/relative canonicals, broken metadata, chaîne de redirectionss, and indexability problèmes à travers the frontend.
- Screaming Frog SEO Spider — explorer with JS rendering on/off to diff raw vs. rendered HTML, and construire pre/post explorer comparisons pour migrations.
- Bing Webmaster Outils — Bing’s Inspection d’URL and où IndexNow submissions apparaître.
Mistakes to éviter on Suivant.js
Concrete patterns que quietly break SEO on réel Suivant.js builds — prevent ces avant ils ship, plutôt que debugging les après a trafic drop.
Shipping sans metadataBase in the root layout
Pourquoi it’s incorrect: sans metadataBase, alternates.canonical and openGraph.images
resolve to relative paths au lieu de absolute URLs. A relative canonical peut confuse
canonicalization signals, and relative OG image URLs break lien previews on social and
messaging apps.
Que faire à la place: définir metadataBase: new URL('https://example.com') une fois in
app/layout.tsx and let every page inherit it. Vérifier it with grep -iE 'rel="canonical"|og:(url|image)' contre a raw HTML récupérer — l’URLs devrait commencer with https://.
Overwriting a layout’s openGraph from a child segment
Pourquoi it’s incorrect: metadata merges shallowly from layout to page. Une page-level
openGraph: { title: 'Home' } doesn’t merge with the layout’s openGraph.images — it
replaces the whole object, silently dropping the images.
Que faire à la place: soit repeat the complet openGraph object (images inclus) at
every level que overrides it, or seulement override the spécifique top-level metadata fields vous
en réalité besoin to modifier, and leave openGraph untouched où the layout’s par défaut is fine.
Exporting metadata from a Client Component
Pourquoi it’s incorrect: the Metadata API is Server Component seulement. Ajouter 'use client' to a fichier
que aussi exports metadata and the export ne fait pashing — aucun error, aucun warning, simplement a
page with aucun title or description.
Que faire à la place: garder metadata / generateMetadata exports in a plain Server
Component fichier (page.tsx or layout.tsx sans 'use client'). Si une page nécessite client
interactivity, put que in a separate child component and import it — don’t ajouter
'use client' to the fichier que owns the metadata export.
Fetching principal content in useEffect (or relying on complet CSR)
Pourquoi it’s incorrect: content rendered seulement in le navigateur isn’t in the initial HTML. Google va eventually render it, but the median render delay is réel and the 90th percentile stretches to hours — and non-Google robots d’exploration (Bing, social preview bots, la plupart AI robots d’exploration) souvent don’t render JavaScript at tout, so ils voir an vide page.
Que faire à la place: par défaut to Server Components, SSG, ISR, or SSR pour anything vous
vouloir indexé. Reserve 'use client' and useEffect data-fetching pour interactive UI que
doesn’t besoin to be crawlable — a filter widget, pas the article corps.
Returning 200 pour a “not found” view
Pourquoi it’s incorrect: rendering a “not found” message sans appel notFound() renvoie a
normal 200 status. That’s a soft 404 — Google peut index the empty-content page au lieu de
recognizing it as manquant, and soft 404s are un of the la plupart courant real-world Suivant.js SEO
problems.
Que faire à la place: appel the built-in notFound() function so the route renvoie a réel
404. Confirmer with curl -I on a known-missing URL and vérifier le code d’état directement,
pas simplement ce que renders in le navigateur.
Skipping generateStaticParams on important dynamic routes
Pourquoi it’s incorrect: sans it, dynamic routes fall back to on-demand rendering (SSR), qui encore fonctionne pour le SEO but adds server latency to every premier requête pour que URL — notamment Googlebot’s.
Que faire à la place: enumerate l’URLs que matter (product pages, blog posts,
category pages) in generateStaticParams() so they’re pre-built, and pair it with
revalidate (ISR) pour content que changements après launch.
Testez vos connaissances: Suivant.js SEO
Five rapide questions on making a Suivant.js site crawlable, indexable, and fast. Pick an réponse pour chaque, alors vérifier.
Ressources utiles
My connexe writing
- JavaScript SEO: A Definitive Guide — my complet guide to rendering, DOM parity, canonicals in JS, sitemaps, and the rendering-mode trade-offs que Suivant.js sits on top of. Ce Suivant.js guide is the framework-specific couche on top of it.
- The Beginner’s Guide to SEO technique — où rendering and exploration fit in the bigger picture.
My speaking
- JavaScript SEO — Ungagged 2019 (SlideShare) — my walkthrough of how frameworks separate frontend from backend and how Googlebot renders. (Standing disclaimer: the dynamic-rendering recommendation in que deck is now outdated — Google deprecated it.)
From autour the industry
- Metadata and OG Images (Suivant.js docs) — the App Router metadata system,
metadataBase, and OG image generation. - generateMetadata API Référence (Suivant.js docs) — static vs. dynamic metadata and the HTML-limited bots / streaming-metadata behavior.
- sitemap.xml Fichier Convention (Suivant.js docs) —
app/sitemap.ts,generateSitemaps(), and localized/image sitemaps. - Courant SEO Problèmes on Suivant.js Websites (Salt Agency) — a 50-site audit study with réel données on soft 404s and LCP échecs.
- How Google Handles JavaScript Throughout the Indexation Traiter (Vercel) — render-timing données from nextjs.org’s propre server beacons.
- The Complet Suivant.js SEO Guide (Strapi) — a thorough framework-walkthrough covering rendering modes, metadata, and données structurées.
- App Router vs Pages Router pour le SEO (Wisp) — a focused comparison of the two routers from an SEO angle.
- r/TechSEO — the community pour rendering/indexation debugging.
Stats worth citing
- Render timing is usually fast, occasionally very slow. Vercel’s analysis of 37 000+ server-beacon pairs on nextjs.org trouvé a median render temps of ~10 seconds, but a 90th percentile of ~3 hours and a 99th percentile of ~18 hours — qui is exactly pourquoi vous don’t vouloir principal content waiting on the render queue. Source
- CWV and soft 404s are widespread on réel Suivant.js sites. Salt Agency’s audit of 50
Suivant.js sites trouvé 41/50 returning soft 404s (404 views at a
200status) and seulement 3/50 passing LCP thresholds — a reminder que the framework’s CWV advantages seulement aider si vous utilisernext/imagewithpriorityand retourner réel code d’états. Source
Journal des modifications
Mis à jour le 18 juil. 2026.
Résumé éditorial et détails enregistrés des changements.Détails des changements
-
Les notes détaillées des changements sont actuellement disponibles en anglais.
-
Les notes détaillées des changements sont actuellement disponibles en anglais.
-
Les notes détaillées des changements sont actuellement disponibles en anglais.
-
Les notes détaillées des changements sont actuellement disponibles en anglais.
Comparaison complète indisponible — aucun instantané antérieur n’a été archivé pour cette révision.