Guide React SEO
React renders client-side by par défaut, so robots d’exploration voir an vide shell jusqu’à JavaScript runs. Here's how Google en réalité processes React apps, qui rendering strategy to pick, and Comment corriger routing, metadata, and the render-timeout duplicate-content trap.
Langues
React n’est pas bad pour le SEO — but rendu côté client by par défaut is. Out of the box (CRA, Vite + React), le serveur ships an vide shell and le navigateur builds lune page, so robots d’exploration voir nothing jusqu’à JavaScript runs. Google peut render React via its Web Rendering Service, but rendering is queued, delayed, and peut temps out — Gary Illyes has affiché render timeouts leaving boilerplate-only pages que obtenir marked as duplicates. AI-crawler rendering contracts vary by provider, so CSR-only content adds a coverage risk. The fix is rendering strategy: SSR or SSG (la plupart easily Suivant.js or Remix) puts content in the initial HTML, hydrated with hydrateRoot (pas createRoot) so server and client output match exactly. Alors utiliser History API routing, réel <a href> liens, correct code d’états, and version-appropriate metadata: React 19 hoists <title>/<meta>/<link> natively, sinon react-helmet-async (jamais the unmaintained original react-helmet).
TL;DR — React n’est pas bad pour le SEO — but the façon la plupart React apps are construit is. By par défaut, React builds lune page in the visitor’s navigateur, so quand a moteur de recherche premier récupère votre URL it obtient a nearly vide page. Google peut usually fill in the blanks by running votre JavaScript, but it’s slower and riskier que simplement handing it finished HTML. The fix is to render votre pages on a server or at construire temps — usually with a framework comme Suivant.js.
Pourquoi React is différent
La plupart websites — a WordPress blog, dire — send the moteur de recherche a complet page:
le serveur builds the HTML and ships it, headline and tout. A standard React app fait
the opposite. Le serveur sends an almost-empty shell (basically an vide <div>),
and alors JavaScript runs in le navigateur to construire the réel page.
That’s great pour slick, app-like experiences. It’s a problem pour le SEO, parce que the premier chose a robot d’exploration downloads is que vide shell. Votre content isn’t là yet — it seulement montre up après the JavaScript runs.
Can’t Google simplement run the JavaScript?
Yes — Google runs a réel, up-to-date version of Chrome behind the scenes and peut execute votre JavaScript to voir the finished page. So React content peut obtenir indexé. Evidence for this claim Googlebot uses an evergreen Chromium rendering engine and can execute JavaScript. Scope: Google Search; successful execution still depends on accessible resources and application behavior. Confidence: high · Verified: Google: JavaScript SEO basics
But là are catches:
- It’s delayed. Google fait the rendering plus tard, in a separate step that’s queued up. So votre content peut prendre plus long to montrer up in search.
- It peut échouer. Si votre page is slow to charger its content, Google’s renderer peut give up avant le contenu apparaît — and index a blank-ish page.
- Autre robots d’exploration vary. Bing handles JavaScript moins reliably, and AI providers ne faites pas publish un shared rendering contract. Quelconque robot d’exploration que récupère seulement the initial HTML va voir a par défaut React app as vide.
The simple fix
Obtenir votre content into the HTML avant it reaches le navigateur. Two façons:
- Rendu côté serveur (SSR) — a server builds the complet page pour chaque requête.
- Static site generation (SSG) — pages are construit into finished HTML ahead of temps. Evidence for this claim React supports server rendering APIs and can be used by frameworks that generate HTML outside the browser. Scope: React server APIs; build-time generation is a framework/build-system capability rather than a React mode by itself. Confidence: high · Verified: React: Server APIs
The easiest chemin to soit is Suivant.js, a framework construit on React que fait ce pour vous. (Remix is un autre bon option.) With SSR or SSG, votre React site hands robots d’exploration a complet page — and it’s as search-friendly as quelconque normal website.
A few autre choses to obtenir correct
- Utiliser normal-looking URLs (
/products), pas hash URLs (/#/products) — Google can’t reliably index the hash ones. - Faire votre liens réel liens (
<a href>), pas clickable<div>s. - Give chaque page its propre title and description que mettre à jour quand lune page changements.
Vouloir the deeper version — how Google’s renderer en réalité fonctionne, the render-timeout trap que creates duplicate pages, the rendering-strategy comparison, and Comment tester ce que Google sees? Switch to the Avancé tab.
TL;DR — React’s SEO problem isn’t React — it’s rendu côté client by par défaut. CRA and Vite + React ship an vide shell and construire the DOM in le navigateur, so the raw HTML a robot d’exploration récupère has aucun content. Google peut render it via the Web Rendering Service (evergreen Chromium), but rendering is queued separately, peut be delayed, and peut temps out — Gary Illyes has documented render timeouts leaving boilerplate-only pages que alors obtenir flagged as duplicates. Bing renders JS moins reliably; AI-crawler rendering varies by provider. The fix is rendering strategy: SSR or SSG (la plupart easily Suivant.js or Remix) puts content in the initial HTML — and si you’re hydrating server-rendered markup, utiliser
hydrateRoot(pascreateRoot) and treat quelconque server/client mismatch as a bug, pas a warning to suppress. Alors utiliser History API routing (pas hash URLs) and réel<a href>liens. Pour<head>metadata: React 19 hoists<title>/<meta>/<link>natively; on React 18 or pour avancé nécessite, utiliser react-helmet-async (jamais the unmaintained original react-helmet). Définir correct HTTP code d’états. There’s aucun ranking bonus pour SSR — it simplement rend content reliably indexable. Pour the general rendering mechanics, voir the JavaScript SEO and headless CMS topics.
Ce que en réalité rend React hard pour le SEO
React is a component-based JavaScript library, and out of the box — Créer React App,
Vite + React — it runs client-side. Le serveur renvoie a near-empty document
(famously simplement a <div id="root"></div>) plus a bundle of JavaScript, and le navigateur
executes que JavaScript to construct the DOM. Contrast que with a server-rendered
page (WordPress, a Rails app), où the complet HTML — content, headings, liens —
arrives in the very premier réponse.
So the question que decides everything is: what’s in the raw HTML avant quelconque JavaScript runs? Pour a par défaut React app, the réponse is “almost nothing.” Right-click → View Source on a CRA app and you’ll voir the shell, pas le contenu. That’s exactly ce que a robot d’exploration obtient on its premier récupérer.
To be precise à propos de où the responsibility en réalité sits: React the library isn’t
CSR-only. React DOM ships client rendering (createRoot), server rendering (streaming
and static APIs), and hydration APIs — the library supports tout of it. The empty-shell
problem is a property of the par défaut toolchain (Créer React App, Vite + React with
aucun server), qui wires up seulement the client APIs and nothing que renders to HTML on the
server. Swap the toolchain — Suivant.js, Remix, or React’s propre server-rendering APIs — and
the même library ships complet HTML on the premier réponse.
Ce is the React-specific application of the broader JavaScript SEO problem — go là pour the general échec modes (parity, interaction, state, timing). Ici I’ll focus on what’s spécifique to React and Comment corriger it.
How Google en réalité processes a React app
Google handles JavaScript in three phases: explorer → render → index. Googlebot récupère l’URL, the rendered DOM is construit plus tard by the Web Rendering Service (WRS) — an evergreen version of Chromium, the même engine as Chrome — and alors the rendered output obtient indexé and its liens extracted. Evidence for this claim Google processes JavaScript pages through crawling, rendering, and indexing using its Web Rendering Service. Scope: Google Search rendering behavior. Confidence: high · Verified: Google: JavaScript SEO basics
The important nuance is quand rendering se produit. Rendering is resource-intensive, so it’s queued separately from the initial explorer. Martin Splitt décrit the flow plainly: “we do an HTTP requête, and we obtenir something back … some barebone HTML and tout it fait is charger the JavaScript and run the JavaScript. Alors, ce HTML … goes into rendering. Rendering runs JavaScript — boom!, a lot of content se produit que wasn’t là avant.” For a CSR React page, the “boom” is votre entier page — none of it exists jusqu’à que render step runs.
A caveat worth carrying: don’t over-index on the old “two waves of indexing” model. Splitt himself walked it back, appel the wave “an oversimplification.” The practical takeaway isn’t “there’s a formal Wave 2 with defined timing” — it’s que rendering is a distinct, deferrable, fallible step, and CSR puts 100% of votre content on the incorrect side of it.
Two plus facts à propos de the renderer que bite React apps specifically:
- It’s stateless. Googlebot ne fait pas retain
localStorage,sessionStorage, or cookies entre page loads. Quelconque content or routing que dépend on client-side state is invisible to the robot d’exploration. - It peut give up. The renderer enforces a timeout. Si votre principal content loads slowly — grand bundles, waterfalls of API calls — rendering peut finish avant votre content arrives, and Google indexes the incomplete page.
The render-timeout trap (and pourquoi it creates duplicates)
Ce échec mode is rarely explained bien, and it’s the la plupart damaging un pour React apps. Gary Illyes décrit it directement: “I have a bunch of emails in my inbox où the problème is que the centerpiece took forever to charger, so rendering timed out … and we were left with a bunch of pages que seulement had the boilerplate. With seulement the boilerplate, ceux pages are dups.”
Walk via ce que que signifie pour a CSR React app. Votre header, nav, and footer are boilerplate que loads fast. Votre réel page content — the partie que rend chaque URL unique — is récupéré and rendered by JavaScript, and it loads slowly. Rendering times out. Google is left with header + nav + footer on every URL. Now every page semble identical, and Google marks les as duplicates of chaque autre in Search Console.
Illyes’s propre fix is the actionable bit: “Essayer to restructure the js calls tel que the content (notamment marginal boilerplate) loads premier and voir si que helps.” But the plus durable réponse is to pas depend on the render step pour votre principal content at tout — qui signifie SSR or SSG.
Rendering strategies pour React
Ce is the unique la plupart impactful decision. The options, roughly worst to meilleur pour le SEO:
- CSR (par défaut React). Server sends the shell; navigateur builds everything. Content is delayed by the render queue and exposed to the timeout. Worst pour le SEO. Fine pour authenticated dashboards vous don’t vouloir indexé anyway. Evidence for this claim Client-only React rendering constructs UI in the browser; server rendering APIs produce HTML before browser hydration. Scope: React rendering mechanics; SEO impact depends on what the initial response contains. Confidence: high · Verified: React: hydrateRoot React: Server APIs
- Pre-rendering. Build-time rendering sans a complet SSR framework — outils comme
react-snapor a prerender service explorer votre app and enregistrer static HTML. Lighter-weight; fonctionne pour simpler, mostly-static sites. - SSG (Static Site Generation). HTML construit une fois at deploy temps and served as static fichiers. Fastest, content toujours présent in the raw HTML. Limited pour highly dynamic or per-user content; grand sites obtenir slow builds.
- SSR (Rendu côté serveur). Le serveur executes React per requête and sends complet HTML. Content immédiatement disponible to robots d’exploration; toujours fresh. Costs a Node.js server and slightly plus élevé TTFB.
- Hybrid / ISR (Incremental Static Regeneration). A Suivant.js fonctionnalité que regenerates static pages in the background — static speed with periodic freshness.
| Strategy | Content in initial HTML? | SEO risk | Meilleur pour |
|---|---|---|---|
| CSR (raw React) | Aucun | Highest | Logged-in dashboards, not-indexed apps |
| Pre-rendering | Yes (construire temps) | Low | Petit, mostly-static sites |
| SSG | Yes (construire temps) | Lowest | Blogs, docs, marketing |
| SSR | Yes (per requête) | Low | Fresh, dynamic content |
| ISR / hybrid | Yes | Low | Content que changements hourly/daily |
And un strategy to skip on nouveau builds: dynamic rendering — detecting the robot d’exploration user-agent and serving it a pre-rendered version pendant que utilisateurs obtenir CSR. Google now calls it “a workaround and not a long-term solution” que “creates additional complexities and resource requirements,” and recommends rendu côté serveur, static rendering, or hydration à la place. (Bing recommended dynamic rendering back in 2018, but que guidance is dated — since 2019 Bingbot renders via Microsoft Edge / Chromium, and SSR/SSG is the correct appel là aussi.)
There’s a myth worth killing ici: SSR n’est pas a ranking boost. As John Mueller put it, “there are no SEO ranking bonuses for implementing it one way or another” — the différent rendering méthodes are “just different ways of making the content indexable.” SSR’s valeur is reliable indexability (and souvent meilleur Core Web Vitals from a faster Premier affichage de contenu), pas a magic ranking lever.
Hydration doit match exactly — that’s a bug boundary, pas an SEO technique
SSR and SSG les deux hand le navigateur HTML que déjà has votre content in it. React alors has to attach to que markup on the client, and that’s a différent API from a plain client render:
createRootrenders React into a DOM node from scratch — aucun existing markup attendu. Utiliser it pour CSR-only apps.hydrateRootattaches React to HTML quereact-dom/serverdéjà generated, and expects the client’s premier render to produce output identical to ce que le serveur sent. Si you’re SSR/SSG, vous vouloirhydrateRoot, pascreateRoot— appelcreateRooton server-rendered markup signifie React discards it and re-renders from scratch, throwing away the exact SEO benefit vous définir up SSR/SSG to obtenir.
Mismatches entre server and client output are a réel risk in React apps doing SEO
fixes — a Date.now() in a title, a locale-dependent format, an
if (typeof window !== 'undefined') branch. React’s propre docs are blunt à propos de ce que
se produit alors: it warns à propos de mismatches in development, but “là are aucun guarantees
que attribute differences va be patched up in cas of mismatches.” The guidance is
to treat mismatches as bugs and fix les — pas to suppress the warning and assume
content parity. Pour le SEO specifically: don’t assume votre rendered content and metadata
match ce que le serveur sent simplement parce que lune page semble correct in le navigateur. Diff the
server HTML contre the post-hydration DOM directement (the View Source vs. Inspect
Element vérifier from the testing section ci-dessous is the fast version of ce) plutôt que
trusting a clean console.
React Router and Structure d’URL
React Router handles navigation in le navigateur sans server round-trips, qui is fine pour le SEO si configuré correctement:
- Utiliser the History API, pas hash routing.
BrowserRouterusespushStateand produces clean, crawlable URLs (/products).HashRouterproduces/#/products, and Google ne peut pas reliably resolve hash-based URLs — the old AJAX-crawling scheme que made les fonctionner is deprecated. Utiliser the History API. - Le serveur doit handle ceux URLs aussi. With History API routing, every “page” nécessite
a réel URL le serveur peut respond to — critical pour SSR, and necessary so a direct hit
or refresh on
/productsdoesn’t 404. <Link>renders a réel anchor. React Router’s<Link>component outputs an<a href>, qui is crawlable. Navigation construit ononClickhandlers sans an anchor is pas crawlable — Google seulement follows réel<a href>liens.
Managing metadata: react-helmet, react-helmet-async, and React 19’s native tags
Via React 18, React jamais mis à jour the document <head> on route changements natively —
every route’s <title>, meta description, canonical, and Ouvrir Graph / Twitter tags had
to be définir by a library. React 19 modifié que: components peut render <title>,
<meta>, and <link> tags directement, and React hoists les to <head> on its propre —
working with client-only apps, streaming SSR, and Server Components alike. React 19,2 is
the current stable release as of mid-2026, so ce now s’applique to quelconque app on a current
React version.
Que signifie the correct réponse dépend on votre React version and ce que vous en réalité besoin:
- React 19, standalone app, seulement basic tags. Render
<title>/<meta>/<link>in votre components directement — aucun library nécessaire. - React 19, but vous besoin
htmlAttributes/bodyAttributes, SSRcontextserialization,onChangeClientState,prioritizeSeoTags, ortitleTemplate. Native hoisting doesn’t cover ces — utiliserreact-helmet-async. Its propre docs appel ce out directement: sans ceux spécifique nécessite, vous may pas besoin the package at tout on React 19. - React 18 or précédent, standalone app. Native hoisting doesn’t exist yet — utiliser
react-helmet-async. It’s actively maintained (major version 3, and it detects votre React version at runtime) and supports SSR. - The original
react-helmet. Don’t utiliser it, on quelconque React version. It’s unmaintained — aucun release since 2020 — and has connu bugs sous React 18’s concurrent rendering. - Suivant.js apps. Utiliser Next’s propre Metadata API (the
metadataexport /generateMetadatain the App Router) regardless of React version — don’t bolt on Helmet, and don’t rely on native React tag hoisting soit. The framework owns the document in a Suivant.js app.
Un reliability rule carries over from JavaScript SEO généralement, and s’applique aucun matter qui of the ci-dessus vous utiliser: HTML-level metadata beats JS-injected metadata. A canonical tag injected by client-side JavaScript is far moins reliable que un présent in the server-rendered HTML — qui is un autre argument pour SSR/SSG.
AI robots d’exploration faire ce urgent
The 2026 wrinkle: rendering behavior pour GPTBot (OpenAI), ClaudeBot (Anthropic), PerplexityBot, and autre agents is provider- and version-specific. A CSR React app ships every robot d’exploration the même vide shell the premier Googlebot récupérer sees, and current provider documentation ne fait pas establish un shared render step que va fill it in. As generative engines become a bigger discovery surface, SSR/SSG arrête being a Google-only concern: raw HTML maximizes coverage sans assuming a universal robot d’exploration limitation. (The CMS headless topic covers ce AI-crawler reality in plus depth.)
Testing ce que Google en réalité sees
Don’t trust votre navigateur — votre DevTools Inspector montre the rendered DOM (post-JavaScript), qui is exactly ce que a crawler-without-JS fait pas voir. Utiliser the correct outils:
- View Source vs. Inspect Element. View Source is the raw HTML (ce que robots d’exploration obtenir avant JS). Inspect Element is the rendered DOM. Si content is in Inspect but manquant from View Source, it’s JavaScript-dependent.
- Inspection d’URL Outil (Search Console) — the la plupart authoritative vérifier. Run a live tester and regarder at the rendered HTML, the screenshot, and the page resources / console messages to voir ce que Google en réalité rendered and ce que failed to charger.
- Résultats enrichis Tester — a rapide rendered-HTML vérifier sans verifying le site.
- Disable JavaScript in DevTools and reload — a fast simulation of a robot d’exploration que doesn’t execute JS (and a decent proxy pour ce que AI robots d’exploration voir).
- Search Console Coverage report — “Discovered, currently not indexed” peut signal a render queue backlog; clusters of duplicate pages peut signal the render-timeout boilerplate trap.
- JS-rendering robots d’exploration — Ahrefs Site Audit and Screaming Frog (JS-rendering mode) render pages at scale so vous pouvez diff raw vs. rendered à travers the whole site.
Suivant.js and Remix (the practical réponse)
Si SEO matters and you’re on raw CSR React, migrating to a framework que renders on the server is usually the correct déplacer. Suivant.js is purpose-built pour ce — SSR and SSG out of the box, ISR, the App Router, a built-in Metadata API, automatic code splitting, and image optimization. Remix is the web-standards alternative, construit on récupérer/Requête/Réponse with SSR by par défaut and a strong progressive-enhancement story. Suivant.js obtient its propre deep dive — I’m keeping it brief ici on objectif. The point pour React SEO is narrower: the framework exists to déplacer votre content out of le navigateur-only render step and into the initial HTML.
React is bon pour le SEO quand vous treat rendering as an architecture decision plutôt que an afterthought. Pick SSR or SSG pour anything que nécessite to rank, garder votre liens and routing honest, manage metadata per route, and let Google’s propre outils — pas votre navigateur — tell vous ce que en réalité rendered.
AI summary
A condensed prendre on the Avancé version:
- React isn’t bad pour le SEO — CSR by par défaut is. React the library supports client,
server, static, and streaming rendering; it’s the par défaut CRA/Vite toolchain (aucun
server) que ships an vide
<div id="root">shell and builds the DOM in the navigateur, so the raw HTML a robot d’exploration récupère has aucun content. - Google peut render React via the Web Rendering Service (evergreen Chromium), but rendering is queued separately, peut be delayed, and is stateless (aucun cookies/localStorage/sessionStorage entre loads).
- The render-timeout trap: si principal content loads slowly, rendering times out and Google indexes a boilerplate-only page. À travers nombreux URLs ceux regarder identical and obtenir flagged as duplicates (Gary Illyes’s documented échec mode). Fix: charger content premier — or meilleur, don’t depend on the render step (SSR/SSG).
- Rendering strategies, meilleur→worst pour le SEO: SSG (lowest risk) ≈ SSR ≈ pre-render > ISR/hybrid > CSR (highest risk). Dynamic rendering is deprecated — Google recommends SSR, static rendering, or hydration.
- Aucun ranking bonus pour SSR — Mueller: “aucun SEO ranking bonuses pour implementing it un façon or un autre.” It simplement rend content reliably indexable.
- Hydration is a bug boundary, pas a technique: SSR/SSG apps hydrate with
hydrateRoot(pascreateRoot), qui expects the client’s premier render to match the server’s exactly. React warns on mismatches in dev but doesn’t guarantee patching les — treat mismatches as bugs and vérifier server vs. post-hydration DOM directement. - React Router: utiliser History API (
BrowserRouter), pas hash routing; le serveur doit handle ceux URLs;<Link>renders crawlable<a href>—onClick-seulement nav doesn’t. - Metadata: React 19 natively hoists
<title>/<meta>/<link>to<head>pour standalone apps needing seulement the basics. On React 18, or pour avancé nécessite (SSR context,titleTemplate), utiliser react-helmet-async — jamais the original react-helmet, unmaintained since 2020. In Suivant.js, utiliser the Metadata API regardless of React version. HTML-level beats JS-injected. - AI-crawler rendering is provider-specific — CSR React dépend on client execution que chaque robot d’exploration may or may pas prise en charge. SSR/SSG puts content in the initial HTML and maximizes coverage.
- Tester with View Source vs. Inspect, Inspection d’URL (rendered HTML + screenshot + console), Résultats enrichis Tester, JS-disabled reload, and a JS-rendering robot d’exploration.
- Suivant.js / Remix are the practical fix — ils déplacer content into the initial HTML.
Documentation officielle
Primary-source documentation from the moteur de recherches.
- Comprendre JavaScript SEO Basics — the explorer → render → index pipeline, SPAs, the History API, URL canoniques with JS, and meaningful Code d’état HTTPs.
- Fix Search-Related JavaScript Problems — soft-404 handling in SPAs, the stateless renderer (aucun cookies/localStorage), and testing with Inspection d’URL.
- Dynamic Rendering (deprecated workaround) — pourquoi Google deprecated it and ce que to utiliser à la place (SSR, static rendering, hydration).
- Introducing a nouveau JavaScript SEO video series — Martin Splitt’s series, covering React, Angular, and Vue specifically.
- In-Depth Guide to How Recherche Google Fonctionne — où rendering sits in explorer → index → serve.
Bing / Microsoft
- The nouveau evergreen Bingbot (Microsoft Edge) — Bingbot rendering JavaScript via the même Chromium platform as Googlebot.
- bingbot Series: JavaScript, Dynamic Rendering, and Cloaking — Bing’s older (2018) prendre; utile pour the historical dynamic-rendering recommendation.
Technical référence
- React v19 (release notes) — native prise en charge pour rendering
<title>,<meta>, and<link>tags in components, hoisted automatically to<head>. - createRoot / hydrateRoot (React docs) — the client-render vs. hydration API split, and the caveat que mismatches aren’t guaranteed to be patched.
- react-helmet-async (npm) — the maintained fork pour managing
<head>in standalone React apps, React 18 or avancé React 19 nécessite.
Quotes from the source
On-the-record statements from Google’s Search team. Chaque search-engine deep lien jumps to the quoted passage on the source page; the staff quotes ci-dessous are lié to the coverage que reproduced les.
Google docs — SPAs and dynamic rendering
- “Single-page applications (SPA) are websites that load an HTML document once and fetch any additional content using JavaScript APIs.” — Recherche Google Central docs. Jump to quote
- “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
Martin Splitt, Google — how JavaScript pages are indexé
- “What we do is we do an HTTP request, and we get something back, right — some HTML, maybe it’s a barebone HTML and all it does is load the JavaScript and run the JavaScript. Then, this HTML that we got from the original HTTP GET request from the crawl, goes into rendering. Rendering runs JavaScript — boom!, a lot of content happens that wasn’t there before.” Lire the coverage (Moteur de recherche Journal)
- On the two-waves model being imprecise: “there’s no such thing as the second wave of crawling-ish. The wave is an oversimplification.” Lire the coverage (Moteur de recherche Roundtable)
Gary Illyes, Google — the render-timeout duplicate-content trap
- “I have a bunch of emails in my inbox where the issue is that the centerpiece took forever to load, so rendering timed out (my most likely explanation) and we were left with a bunch of pages that only had the boilerplate. With only the boilerplate, those pages are dups.”
- “Do you have a JavaScript-heavy site and you see lots of dups reported in Search Console? Try to restructure the js calls such that the content (including marginal boilerplate) loads first and see if that helps.” Lire the post (LinkedIn)
John Mueller, Google — aucun ranking bonus pour rendering choice
- “There are no SEO ranking bonuses for implementing it one way or another.” They’re “just different ways of making the content indexable (as is client side rendering).” Lire the coverage (Moteur de recherche Roundtable)
React SEO checklist
A réussir to confirmer robots d’exploration peut voir and index votre React app:
- Important content apparaît in View Source (raw HTML), pas seulement in the rendered DOM — si it’s manquant, you’re selon CSR.
- Pages que besoin to rank utiliser SSR or SSG (Suivant.js, Remix, or a pre-render step), pas raw rendu côté client.
- Principal content loads fast and premier — aucun slow API waterfalls que pourrait trip the render timeout into a boilerplate-only page.
- Routing uses the History API (
BrowserRouter), pasHashRouter/#/URLs. - The server peut respond to every client-side route (aucun 404 on direct hit or refresh).
- Navigation uses réel
<a href>liens (React Router<Link>), pasonClick-seulement handlers on<div>/<button>. - Chaque route sets a unique
<title>, meta description, canonical, and OG tags que mettre à jour on navigation. - Metadata matches votre version: React 19 native
<title>/<meta>/<link>tags pour the basics, react-helmet-async pour React 18 or avancé nécessite (jamais the deprecatedreact-helmet), or the Suivant.js Metadata API si on Suivant.js. - Si server-rendered, you’re hydrating with
hydrateRoot(pascreateRoot), and quelconque dev-mode hydration mismatch warning is treated as a bug to fix. - Client-side “not found” routes retourner a réel 404 (or a
noindex), pas a soft-404 with a200status. - JavaScript and CSS ne sont pas blocked in
robots.txt(Google won’t render from blocked fichiers). - Aucun critical content dépend on cookies / localStorage / sessionStorage (the renderer is stateless).
- Verified in Inspection d’URL: the rendered HTML and screenshot montrer votre contenu réel.
- Coverage report vérifié pour “Discovered, currently not indexed” (render backlog) and duplicate clusters (render-timeout trap).
The mental models
1. The seulement question que matters: what’s in the raw HTML? View Source avant quelconque JavaScript runs is ce que a first-fetch robot d’exploration — and la plupart AI robots d’exploration, forever — voir. Si votre content isn’t là, vous have a React SEO problem regardless of how bon lune page semble in votre navigateur.
2. Rendering is a separate, fallible step. Explorer → render → index. CSR puts 100% of votre content on the far side of the render step, qui is queued, delayed, stateless, and peut temps out. SSR/SSG déplacer votre content avant que step. Don’t over-trust the “two waves” model — Splitt himself appelé it an oversimplification.
3. The boilerplate-duplicate échec mode. Slow content + render timeout = every URL renders as header/nav/footer seulement = Google sees duplicates. The fix is structural: charger content premier, or arrêter selon the render step.
4. The rendering decision tree.
- Public content que doit rank or be cited by AI → SSG (static) or SSR (fresh).
- Mostly static (blog, docs, marketing) → SSG, or ISR on a timer.
- Frequently modification, doit be fresh → SSR.
- Logged-in dashboard, pas meant to be indexé → CSR is fine.
- Nouveau construire que nécessite SEO → reach pour Suivant.js / Remix, pas dynamic rendering.
5. HTML-first, JS-second pour every SEO signal. Content, liens, canonicals, titles, données structurées — obtenir les into le serveur-rendered HTML. Treat JS-injected SEO signals (notamment JS balise canonicals and react-helmet on CSR) as a fallback, pas the plan: Google sees les late, and AI robots d’exploration pas at tout.
React SEO — cheat sheet
Rendering modes En un coup d’œil
| Mode | Content in initial HTML? | SEO risk | Utiliser pour |
|---|---|---|---|
| CSR (raw React) | Aucun | Highest | Logged-in dashboards, not-indexed apps |
| Pre-rendering (react-snap) | Yes (construire temps) | Low | Petit, mostly-static sites |
| SSG | Yes (construire temps) | Lowest | Blogs, docs, marketing |
| SSR | Yes (per requête) | Low | Fresh, dynamic content |
| ISR / hybrid (Suivant.js) | Yes | Low | Hourly/daily content |
| Dynamic rendering | Bot-only | Deprecated | Don’t — utiliser SSR/SSG/hydration |
Courant React SEO mistakes → fixes
| Mistake | Fix |
|---|---|
| Content seulement in rendered DOM (CSR) | SSR / SSG / pre-render |
Hash URLs (/#/path) | History API (BrowserRouter) |
onClick navigation, aucun anchor | Réel <a href> / React Router <Link> |
| Meta tags don’t mettre à jour on route | React 19: native <title>/<meta>/<link>. Older/avancé: react-helmet-async. Suivant.js: Metadata API |
react-helmet (original, quelconque React version) | Switch to react-helmet-async or React 19 native tags |
createRoot utilisé on server-rendered HTML | Utiliser hydrateRoot à la place — createRoot discards le serveur markup |
| Hydration mismatch warning suppressed | Treat it as a bug and fix le serveur/client difference |
| Slow content → render timeout → dups | Charger principal content premier; déplacer to SSR/SSG |
Client-side 404 renvoie 200 | Réel 404 status or noindex |
Blocked .js / .css in robots.txt | Autoriser les — Google won’t render blocked fichiers |
| Content gated on cookies/localStorage | Don’t — the renderer is stateless |
Fast rules
- The renderer is evergreen Chromium, queued, stateless, and times out.
- Aucun ranking bonus pour SSR — it’s à propos de reliable indexability (Mueller).
- AI-crawler rendering varies by provider → raw HTML is the safest coverage baseline.
- In Suivant.js, utiliser the Metadata API — pas React Helmet.
- Bing renders JS (via Edge) but moins reliably; SSR/SSG is the safe appel là aussi.
Vérifier ce que le serveur sends avant React runs
Put representative indexable routes in urls.txt. Ce trouve CSR shells and manquant
server-rendered head markup in the raw réponse:
while IFS= read -r url; do
html=$(mktemp)
status=$(curl -sS -o "$html" -w '%{http_code}' "$url")
bytes=$(wc -c < "$html" | tr -d ' ')
title_count=$(grep -Eio '<title>[^<]*</title>' "$html" | wc -l | tr -d ' ')
canonical_count=$(grep -Eio '<link[^>]+rel=["'"']canonical["'"'][^>]*>' "$html" | wc -l | tr -d ' ')
printf '%s\t%s\tbytes=%s\ttitles=%s\tcanonicals=%s\n' "$status" "$url" "$bytes" "$title_count" "$canonical_count"
rm -f "$html"
done < urls.txtPetit byte size is seulement a examiner signal, pas an error by itself. Comparer flagged raw réponses with rendered HTML and confirmer que principal text and crawlable liens are présent.
Outils pour auditing React SEO
- View Source vs. Inspect Element — the fastest premier vérifier. View Source is the raw HTML (ce que a robot d’exploration obtient avant JS); Inspect Element is the rendered DOM. Content in Inspect but pas View Source is JavaScript-dependent.
- Inspection d’URL (Recherche Google Console) — the source of truth. Run a live tester, alors view the rendered HTML, the screenshot, the page resources (ce que chargé vs. was blocked), and console messages to voir exactly ce que Google rendered.
- Résultats enrichis Tester — a rapide rendered-HTML and structured-data vérifier pour a unique URL sans verifying le site.
- Chrome DevTools — disable JavaScript (Command Menu → “Disable JavaScript”) and reload to inspect ce que an HTML-only fetcher receives. Ce is a coverage vérifier, pas proof of quelconque named AI crawler’s current rendering behavior.
- Search Console Coverage report — watch “Discovered, currently not indexed” (render backlog) and duplicate clusters (the render-timeout boilerplate trap).
- JS-rendering robots d’exploration — Ahrefs Site Audit and Screaming Frog SEO Spider (JS-rendering mode) execute JavaScript so vous pouvez diff raw vs. rendered HTML à travers le site.
Mistakes React teams en réalité faire
Concrete patterns I garder seeing in CSR React apps que obtenir shipped, pas hypothetical ones. Chaque is a prevention déplacer — catch it avant it costs vous indexation.
Shipping raw CRA/Vite CSR pour pages que besoin to rank
Teams ship Créer React App or Vite + React straight to production pour marketing pages,
blog posts, or product pages — the exact content que nécessite to montrer up in search.
Pourquoi it’s incorrect: le serveur sends an almost-empty <div id="root"> shell; votre réel
content seulement exists après JavaScript runs, so it’s delayed by Google’s render queue and
peut be invisible to quelconque AI robot d’exploration que récupère initial HTML sans client execution.
Que faire à la place: déplacer
anything que nécessite to rank or be cited to SSR or SSG — Suivant.js or Remix are the easiest
paths — and reserve raw CSR pour logged-in, non-indexed surfaces comme dashboards.
Routing on hash URLs (HashRouter)
Reaching pour React Router’s HashRouter parce que it’s the chemin of least resistance —
aucun server config nécessaire, fonctionne on quelconque static host. Pourquoi it’s incorrect: Google can’t
reliably resolve /#/products-style URLs; the old AJAX-crawling scheme que made hash
fragments crawlable is deprecated. Que faire à la place: utiliser BrowserRouter (the
History API) and assurez-vous le serveur responds to every route it produces, notamment a
direct hit or refresh on a deep lien.
Building navigation on onClick au lieu de réel anchors
Wiring up navigation with onClick handlers on a <div> or <button>, souvent parce que it
was easier to style or éviter par défaut lien behavior. Pourquoi it’s incorrect: Google seulement
follows réel <a href> liens — a <div> with a click handler is invisible to exploration,
aucun matter how it behaves pour a mouse. Que faire à la place: utiliser React Router’s
<Link> component, qui renders an réel <a href> sous the hood, or a plain anchor
tag pour external navigation.
Chargement principal content behind a slow API waterfall
Fetching the header and nav fast, alors chaining several API calls avant the réel page content — the partie que rend chaque URL unique — apparaît. Pourquoi it’s incorrect: Google’s Web Rendering Service enforces a timeout; si votre centerpiece content loads slowly, rendering finishes avant it arrives, and Google is left indexation boilerplate-only pages que alors obtenir flagged as duplicates of chaque autre — the exact échec mode Gary Illyes décrit. Que faire à la place: restructure requêtes so principal content loads premier, or supprimer the dependency on rendu côté client entirely with SSR/SSG.
Encore en utilisant the original react-helmet
Reaching pour react-helmet pour per-route <title> and meta tags parce que it’s the
library every older tutorial recommends. Pourquoi it’s incorrect: the original package is
unmaintained — aucun release since 2020 — and has connu problèmes sous React 18’s concurrent
rendering and SSR. Que faire à la place: on React 19, render <title>/<meta>/<link>
directement in votre components and let React hoist les (aucun library nécessaire pour the basics).
On React 18, or pour avancé nécessite comme SSR context serialization or titleTemplate, utiliser
react-helmet-async, the maintained fork. On Suivant.js, utiliser its built-in Metadata API and
don’t bolt Helmet on top of it.
Blocking JavaScript or CSS in robots.txt
Blocking /static/js/ or a bundler’s asset folder in robots.txt, parfois left over
from an old crawl-budget concern or copied from un autre site’s config. Pourquoi it’s
incorrect: Google can’t render Ce que c’estn’t allowed to récupérer — a blocked bundle signifie the
Web Rendering Service builds an incomplete (or vide) DOM, même though votre code source
is fine. Que faire à la place: autoriser robots d’exploration to récupérer votre JS and CSS, and confirmer it
with the Inspection d’URL tool’s page-resources vérifier to voir nothing critical is blocked.
Testez vos connaissances: React SEO
Five rapide questions on making React apps crawlable and indexable. Pick an réponse pour chaque, alors vérifier.
Ressources utiles
My connexe writing
- React SEO: Meilleur Practices to Faire It SEO-Friendly (Ahrefs) — the React SEO guide on Ahrefs, qui I reviewed; ce article is the deeper, source-linked treatment.
- JavaScript SEO: A Definitive Guide (Ahrefs) — my complet guide to the underlying rendering mechanics: DOM parity, the most-restrictive-directive rule, canonical and meta-tag handling, and rendering choices. Lire ce pour the general cas behind React-specific fixes.
- The Beginner’s Guide to SEO technique (Ahrefs) — où React/JavaScript SEO fits in the bigger picture.
My speaking
- How Search Fonctionne (SlideShare) — my walkthrough of exploration, rendering, indexation, and ranking. (My standing disclaimer s’applique: “This is my understanding of systems… not going to be 100% complete or accurate.”)
From autour the industry
- Comprendre JavaScript SEO Basics (Google) — primary-source docs on SPAs, the History API, and canonical/status-code handling.
- Dynamic Rendering (deprecated) (Google) — pourquoi dynamic rendering is a workaround, pas a long-term solution.
- Martin Splitt Explique How JavaScript Sites Are Indexé (Moteur de recherche Journal) — the “barebone HTML … boom” explanation of the explorer → render → index flow.
- Gary Illyes on JS-heavy sites and contenu dupliqué (LinkedIn) — the render-timeout-to-duplicates échec mode, in his propre words.
- Comment corriger SEO technique problèmes on client-side React apps (Moteur de recherche Land) — a real-world cas study on auditing and fixing a CSR React app.
- SSR vs. dynamic rendering — aucun ranking difference (Moteur de recherche Roundtable) — Mueller’s “no SEO ranking bonuses” statement.
- react-helmet-async (npm) — the maintained head-management library pour standalone React.
- The nouveau evergreen Bingbot (Microsoft Edge) (Bing) — Bingbot rendering JS via Chromium, comme Googlebot.
Videos
- Recherche Google Central — JavaScript SEO series (YouTube) — Martin Splitt’s official video series covers SEO pour React, Angular, and Vue specifically, walking via the explorer → render → index traiter and the courant fixes. Series announcement · Channel
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.
Comparaison complète indisponible — aucun instantané antérieur n’a été archivé pour cette révision.