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.

Première publication : 26 juin 2026 · Dernière mise à jour : 3 août 2026 · Advanced
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’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 (pas createRoot) 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-snap or 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.
StrategyContent in initial HTML?SEO riskMeilleur pour
CSR (raw React)AucunHighestLogged-in dashboards, not-indexed apps
Pre-renderingYes (construire temps)LowPetit, mostly-static sites
SSGYes (construire temps)LowestBlogs, docs, marketing
SSRYes (per requête)LowFresh, dynamic content
ISR / hybridYesLowContent 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:

  • createRoot renders React into a DOM node from scratch — aucun existing markup attendu. Utiliser it pour CSR-only apps.
  • hydrateRoot attaches React to HTML que react-dom/server dé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 vouloir hydrateRoot, pas createRoot — appel createRoot on 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.
Evidence for this claim hydrateRoot attaches React to HTML previously generated by React on the server; the initial client output should match the server output. Scope: hydration Confidence: high · Verified: hydrateRoot

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. BrowserRouter uses pushState and produces clean, crawlable URLs (/products). HashRouter produces /#/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 /products doesn’t 404.
  • <Link> renders a réel anchor. React Router’s <Link> component outputs an <a href>, qui is crawlable. Navigation construit on onClick handlers 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, SSR context serialization, onChangeClientState, prioritizeSeoTags, or titleTemplate. Native hoisting doesn’t cover ces — utiliser react-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 metadata export / generateMetadata in 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.

Add an expert note

Pin an expert quote

New person? Create their unclaimed profile at /admin/experts/ → Pin a quote first.