SEO React

React renders client-side by domyślny, so crawlers see an empty shell until JavaScript runs. Here's how Google actually procesy React apps, który renderowanie strategy to pick, i how to fix routing, metadata, i the render-timeout duplicate-treść trap.

Opublikowano po raz pierwszy: 26 cze 2026 · Ostatnia aktualizacja: 3 sie 2026 · Advanced
Języki

React jest nie bad dla SEO — ale client-side renderowanie by domyślny jest. Out of the box (CRA, Vite + React), the serwer ships an empty shell i the przeglądarka builds the strona, so crawlers see nothing until JavaScript runs. Google może render React via jego Web renderowanie Service, ale renderowanie jest queued, delayed, i może time out — Gary Illyes ma shown render timeouts leaving boilerplate-tylko strony że get marked as duplicates. AI-crawler renderowanie contracts vary by provider, so CSR-tylko treść dodaje a coverage risk. The fix jest renderowanie strategy: SSR lub SSG (najbardziej easily następny.js lub Remix) puts treść in the initial HTML, hydrated z hydrateRoot (nie createRoot) so serwer i client output match exactly. Then używać History API routing, rzeczywisty <a href> links, poprawny kody stanu, i version-appropriate metadata: React 19 hoists <title>/<meta>/<link> natively, otherwise react-helmet-async (nigdy the unmaintained original react-helmet).

TL;DR — React’s SEO problem isn’t React — it’s client-side renderowanie by domyślny. CRA i Vite + React ship an empty shell i zbuduj DOM in the przeglądarka, so the raw HTML a crawler fetches ma no treść. Google może render it via the Web renderowanie Service (evergreen Chromium), ale renderowanie jest queued osobno, może być delayed, i może time out — Gary Illyes ma udokumentowany render timeouts leaving boilerplate-tylko strony że then get flagged as duplicates. Bing renders JS mniej reliably; AI-crawler renderowanie varies by provider. The fix jest renderowanie strategy: SSR lub SSG (najbardziej easily następny.js lub Remix) puts treść in the initial HTML — i if you’re hydrating serwer-wyrenderowany znaczniki, używać hydrateRoot (nie createRoot) i treat dowolny serwer/client mismatch as a bug, nie a ostrzeżenie to suppress. Then używać History API routing (nie hash URLs) i rzeczywisty <a href> links. dla <head> metadata: React 19 hoists <title>/<meta>/<link> natively; on React 18 lub dla advanced needs, używać react-helmet-async (nigdy the unmaintained original react-helmet). ustawić poprawny HTTP kody stanu. There’s no ranking bonus dla SSR — it just makes treść reliably indeksowalny. dla the general renderowanie mechanics, see the SEO JavaScript i headless CMS topics.

co actually makes React trudny dla SEO

React jest a component-oparty JavaScript library, i out of the box — Create React App, Vite + React — it runs client-side. The serwer zwroty a near-empty document (famously just a <div id="root"></div>) plus a bundle of JavaScript, i the przeglądarka executes że JavaScript to construct the DOM. Contrast że z a serwer-wyrenderowany strona (WordPress, a Rails app), gdzie the pełny HTML — treść, nagłówki, links — arrives in the bardzo pierwszy odpowiedź.

So the question że decides everything jest: co’s in the raw HTML przed dowolny JavaScript runs? dla a domyślny React app, the answer jest “almost nothing.” Right-click → View źródło on a CRA app i you’ll see the shell, nie the treść. że’s exactly co a crawler gets on jego pierwszy fetch.

To być precise o gdzie the responsibility actually sits: React the library isn’t CSR-tylko. React DOM ships client renderowanie (createRoot), serwer renderowanie (streaming i statyczny APIs), i hydration APIs — the library obsługuje wszystkie of it. The empty-shell problem jest a właściwość of the domyślny toolchain (Create React App, Vite + React z no serwer), który wires up tylko the client APIs i nothing że renders to HTML on the serwer. Swap the toolchain — następny.js, Remix, lub React’s own serwer-renderowanie APIs — i the same library ships pełny HTML on the pierwszy odpowiedź.

ten jest the React-specific application of the broader SEO JavaScript problem — go there dla the general awaria modes (parity, interaction, state, timing). Here I’ll focus on co’s specific to React i how to fix it.

How Google actually procesy a React app

Google handles JavaScript in three phases: crawl → render → index. Googlebot fetches the URL, the wyrenderowany DOM jest built later by the Web renderowanie Service (WRS) — an evergreen version of Chromium, the same engine as Chrome — i then the wyrenderowany output gets zindeksowany i jego links 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 ważny nuance jest gdy renderowanie happens. renderowanie jest zasób-intensive, so it’s queued osobno z the initial crawl. Martin Splitt described the flow plainly: “we robić an HTTP żądanie, i we get something back … niektóre barebone HTML i wszystkie it robi jest load the JavaScript i run the JavaScript. Then, ten HTML … goes do renderowanie. renderowanie runs JavaScript — boom!, a lot of treść happens że wasn’t there przed.” For a CSR React page, the “boom” jest twój entire strona — none of it exists until że render krok runs.

A caveat worth carrying: don’t ponad-index on the old “two waves of indexing” model. Splitt himself walked it back, calling the wave “an oversimplification.” The practical takeaway isn’t “there’s a formal Wave 2 with defined timing” — it’s że renderowanie jest a distinct, deferrable, fallible krok, i CSR puts 100% of twój treść on the błędny side of it.

Two więcej fakty o the renderer że bite React apps specifically:

  • It’s stateless. Googlebot robi nie retain localStorage, sessionStorage, lub cookies między strona loads. dowolny treść lub routing że depends on client-side state jest invisible to the crawler.
  • It może give up. The renderer enforces a timeout. If twój main treść loads slowly — duży bundles, waterfalls of API calls — renderowanie może finish przed twój treść arrives, i Google indexes the incomplete strona.

The render-timeout trap (i why it creates duplicates)

ten awaria mode jest rarely explained well, i it’s the najbardziej damaging one dla React apps. Gary Illyes described it directly: “I mieć a bunch of emails in my inbox gdzie the problem jest że the centerpiece took forever to load, so renderowanie timed out … i we były left z a bunch of strony że tylko miał the boilerplate. z tylko the boilerplate, tamte strony są dups.”

Walk przez co że means dla a CSR React app. twój header, nav, i footer są boilerplate że loads fast. twój rzeczywisty strona treść — the part że makes każdy URL unique — jest fetched i wyrenderowany by JavaScript, i it loads slowly. renderowanie times out. Google jest left z header + nav + footer on każdy URL. Now każdy strona looks identical, i Google marks them as duplicates of każdy other in Search Console.

Illyes’s own fix jest the actionable bit: “Try to restructure the js calls such że the treść (w tym marginal boilerplate) loads pierwszy i see if że pomaga.” ale the więcej durable answer jest to nie depend on the render krok dla twój main treść at wszystkie — który means SSR lub SSG.

renderowanie strategies dla React

ten jest the single najbardziej impactful decision. The options, roughly worst to best dla SEO:

  • CSR (domyślny React). serwer wysyła the shell; przeglądarka builds everything. treść jest delayed by the render queue i exposed to the timeout. Worst dla SEO. Fine dla authenticated panele you don’t want zindeksowany 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-renderowanie. Build-time renderowanie bez a pełny SSR framework — narzędzia like react-snap lub a prerender service crawl twój app i save statyczny HTML. Lighter-weight; działa dla simpler, mostly-statyczny witryny.
  • SSG (statyczny witryna Generation). HTML built once at deploy time i served as statyczny files. Fastest, treść zawsze present in the raw HTML. Limited dla highly dynamiczny lub per-użytkownik treść; duży witryny get slow builds.
  • SSR (serwer-Side renderowanie). The serwer executes React per żądanie i wysyła pełny HTML. treść immediately available to crawlers; zawsze fresh. Costs a Node.js serwer i slightly higher TTFB.
  • Hybrid / ISR (Incremental statyczny Regeneration). A następny.js funkcja że regenerates statyczny strony in the background — statyczny speed z periodic freshness.
Strategytreść in initial HTML?SEO riskBest dla
CSR (raw React)NoHighestLogged-in panele, nie-zindeksowany apps
Pre-renderowanieYes (build time)niskimały, mostly-statyczny witryny
SSGYes (build time)LowestBlogs, docs, marketing
SSRYes (per żądanie)niskiFresh, dynamiczny treść
ISR / hybridYesniskitreść że changes hourly/daily

i one strategy to skip on new builds: dynamiczny renderowanie — detecting the crawler użytkownik-agent i serving it a pre-wyrenderowany version podczas gdy użytkownicy get CSR. Google now calls it “a workaround and not a long-term solution” że “creates additional complexities i zasób requirements,” i recommends serwer-side renderowanie, statyczny renderowanie, lub hydration instead. (Bing recommended dynamiczny renderowanie back in 2018, ale że guidance jest dated — since 2019 Bingbot renders via Microsoft Edge / Chromium, i SSR/SSG jest the right call there too.)

There’s a myth worth killing here: SSR jest nie a ranking boost. As John Mueller put it, “there are no SEO ranking bonuses for implementing it one way or another” — the różny renderowanie metody są “just different ways of making the content indexable.” SSR’s wartość jest niezawodny indexability (i często better Core Web Vitals z a faster pierwszy Contentful Paint), nie a magic ranking lever.

Hydration musi match exactly — że’s a bug granica, nie an SEO technique

SSR i SSG oba hand the przeglądarka HTML że już ma twój treść in it. React then ma to attach to że znaczniki on the client, i że’s a różny API z a plain client render:

  • createRoot renders React do a DOM node z scratch — no existing znaczniki expected. używać it dla CSR-tylko apps.
  • hydrateRoot attaches React to HTML że react-dom/server już generated, i expects the client’s pierwszy render to produce output identical to co the serwer sent. If you’re SSR/SSG, you want hydrateRoot, nie createRoot — calling createRoot on serwer-wyrenderowany znaczniki means React discards it i re-renders z scratch, throwing away the dokładny SEO benefit you ustawić up SSR/SSG to get.
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 między serwer i client output są a rzeczywisty risk in React apps doing SEO fixes — a Date.now() in a tytuł, a locale-dependent format, an if (typeof window !== 'undefined') branch. React’s own docs są blunt o co happens then: it warns o mismatches in development, ale “there są no guarantees że atrybut differences będzie być patched up in case of mismatches.” The guidance jest to treat mismatches as bugs i fix them — nie to suppress the ostrzeżenie i assume treść parity. dla SEO specifically: don’t assume twój wyrenderowany treść i metadata match co the serwer sent just ponieważ the strona looks right in the przeglądarka. Diff the serwer HTML wobec the post-hydration DOM directly (the View źródło vs. Inspect element sprawdzenie z the testing sekcja below jest the fast version of ten) zamiast trusting a clean console.

React Router i struktura URL

React Router handles navigation in the przeglądarka bez serwer round-trips, który jest fine dla SEO if configured correctly:

  • użyj History API, nie hash routing. BrowserRouter używa pushState i produces clean, crawlable URLs (/products). HashRouter produces /#/products, i Google cannot reliably resolve hash-oparty URLs — the old AJAX-crawling scheme że made them działać jest deprecated. użyj History API.
  • The serwer musi handle tamte URLs too. z History API routing, każdy “page” needs a rzeczywisty URL the serwer może respond to — critical dla SSR, i necessary so a bezpośredni hit lub refresh on /products doesn’t 404.
  • <Link> renders a rzeczywisty anchor. React Router’s <Link> component outputs an <a href>, który jest crawlable. Navigation built on onClick handlers bez an anchor jest nie crawlable — Google tylko follows rzeczywisty <a href> links.

Managing metadata: react-helmet, react-helmet-async, i React 19’s native znaczniki

przez React 18, React nigdy updated the document <head> on route changes natively — każdy route’s <title>, opis meta, canonical, i otwarty Graph / Twitter znaczniki miał to być ustawić by a library. React 19 changed że: components może render <title>, <meta>, i <link> znaczniki directly, i React hoists them to <head> on jego own — działający z client-tylko apps, streaming SSR, i serwer Components alike. React 19,2 jest the current stable release as of mid-2026, so ten now applies to dowolny app on a current React version.

że means the right answer depends on twój React version i co you actually need:

  • React 19, standalone app, tylko basic znaczniki. Render <title>/<meta>/<link> in twój components directly — no library needed.
  • React 19, ale you need htmlAttributes/bodyAttributes, SSR context serialization, onChangeClientState, prioritizeSeoTags, lub titleTemplate. Native hoisting doesn’t cover te — używać react-helmet-async. jego own docs call ten out directly: bez tamte specific needs, you może nie need the package at wszystkie on React 19.
  • React 18 lub earlier, standalone app. Native hoisting doesn’t exist yet — używać react-helmet-async. It’s actively maintained (major version 3, i it detects twój React version at runtime) i obsługuje SSR.
  • The original react-helmet. Don’t używać it, on dowolny React version. It’s unmaintained — no release since 2020 — i ma znany bugs poniżej React 18’s concurrent renderowanie.
  • następny.js apps. używać następny’s own Metadata API (the metadata export / generateMetadata in the App Router) regardless of React version — don’t bolt on Helmet, i don’t rely on native React znacznik hoisting either. The framework owns the document in a następny.js app.

One reliability reguła carries ponad z SEO JavaScript generally, i applies no matter który of the above you używać: HTML-level metadata beats JS-injected metadata. A canonical znacznik injected by client-side JavaScript jest far mniej niezawodny than one present in the serwer-wyrenderowany HTML — który jest another argument dla SSR/SSG.

AI crawlers make ten urgent

The 2026 wrinkle: renderowanie behavior dla GPTBot (OpenAI), ClaudeBot (Anthropic), PerplexityBot, i other agents jest provider- i version-specific. A CSR React app ships każdy crawler the same empty shell the pierwszy Googlebot fetch sees, i current provider documentation robi nie establish one shared render krok że będzie fill it in. As generative engines become a bigger discovery surface, SSR/SSG stops będąc a Google-tylko concern: raw HTML maximizes coverage bez assuming a universal crawler limitation. (The headless CMS topic covers ten AI-crawler reality in więcej depth.)

Testing co Google actually sees

Don’t trust twój przeglądarka — twój DevTools Inspector pokazuje the wyrenderowany DOM (post-JavaScript), który jest exactly co a crawler-bez-JS robi nie see. użyj right narzędzia:

  • View źródło vs. Inspect element. View źródło jest the raw HTML (co crawlers get przed JS). Inspect element jest the wyrenderowany DOM. If treść jest in Inspect ale missing z View źródło, it’s JavaScript-dependent.
  • URL Inspection narzędzie (Search Console) — the najbardziej authoritative sprawdzenie. Run a live test i look at the wyrenderowany HTML, the screenshot, i the strona zasoby / console messages to see co Google actually wyrenderowany i co failed to load.
  • wyniki z elementami rozszerzonymi Test — a quick wyrenderowany-HTML sprawdzenie bez verifying the witryna.
  • Disable JavaScript in DevTools i reload — a fast simulation of a crawler że doesn’t execute JS (i a decent proxy dla co AI crawlers see).
  • Search Console Coverage raport — “Discovered, currently not indexed” może signal a render queue backlog; clusters of duplicate strony może signal the render-timeout boilerplate trap.
  • JS-renderowanie crawlers — Ahrefs witryna Audit i Screaming Frog (JS-renderowanie mode) render strony at scale so you może diff raw vs. wyrenderowany w całym the whole witryna.

następny.js i Remix (the practical answer)

If SEO matters i you’re on raw CSR React, migrating to a framework że renders on the serwer jest zwykle the right move. następny.js jest purpose-built dla ten — SSR i SSG out of the box, ISR, the App Router, a built-in Metadata API, automatic code splitting, i image optymalizacja. Remix jest the web-standardy alternative, built on fetch/żądanie/odpowiedź z SSR by domyślny i a strong progressive-enhancement story. następny.js gets jego own deep dive — I’m keeping it brief here on purpose. The point dla React SEO jest narrower: the framework exists to move twój treść out of the przeglądarka-tylko render krok i do the initial HTML.

React jest good dla SEO gdy you treat renderowanie as an architektura decision zamiast an afterthought. Pick SSR lub SSG dla anything że needs to rank, zachować twój links i routing honest, manage metadata per route, i let Google’s own narzędzia — nie twój przeglądarka — tell you co actually wyrenderowany.

Add an expert note

Pin an expert quote

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