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.
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 jest nie bad dla SEO — ale the way najbardziej React apps są built jest. By domyślny, React builds the strona in the visitor’s przeglądarka, so gdy a wyszukiwarka pierwszy fetches twój URL it gets a nearly empty strona. Google może zwykle fill in the blanks by running twój JavaScript, ale it’s slower i riskier than just handing it finished HTML. The fix jest to render twój strony on a serwer lub at build time — zwykle z a framework like następny.js.
Why React jest różny
najbardziej websites — a WordPress blog, say — wysyłać the wyszukiwarka a complete strona:
the serwer builds the HTML i ships it, headline i wszystkie. A standard React app robi
the opposite. The serwer wysyła an almost-empty shell (basically an empty <div>),
i then JavaScript runs in the przeglądarka to zbuduj rzeczywisty strona.
że’s great dla slick, app-like experiences. It’s a problem dla SEO, ponieważ the pierwszy thing a crawler downloads jest że empty shell. twój treść isn’t there yet — it tylko pokazuje up po the JavaScript runs.
może’t Google just run the JavaScript?
Yes — Google runs a rzeczywisty, up-to-date version of Chrome behind the scenes i może execute twój JavaScript to see the finished strona. So React treść może get zindeksowany. 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
ale there są catches:
- It’s delayed. Google robi the renderowanie later, in a oddzielny krok że’s queued up. So twój treść może take longer to pokazywać up in search.
- It może fail. If twój strona jest slow to load jego treść, Google’s renderer może give up przed the treść appears — i index a blank-ish strona.
- Other crawlers vary. Bing handles JavaScript mniej reliably, i AI providers robić nie publikować one shared renderowanie contract. dowolny crawler że fetches tylko the initial HTML będzie see a domyślny React app as empty.
The prosty fix
Get twój treść do the HTML przed it reaches the przeglądarka. Two ways:
- serwer-side renderowanie (SSR) — a serwer builds the pełny strona dla każdy żądanie.
- statyczny witryna generation (SSG) — strony są built do finished HTML ahead of time. 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 path to either jest następny.js, a framework built on React że robi ten dla you. (Remix jest another good option.) z SSR lub SSG, twój React witryna hands crawlers a complete strona — i it’s as search-friendly as dowolny normal website.
A kilka other things to get right
- używać normal-looking URLs (
/products), nie hash URLs (/#/products) — Google może’t reliably index the hash ones. - Make twój links rzeczywisty links (
<a href>), nie clickable<div>s. - Give każdy strona jego own tytuł i opis że update gdy the strona changes.
Want the deeper version — how Google’s renderer actually działa, the render-timeout trap że creates duplicate strony, the renderowanie-strategy comparison, i how to test co Google sees? Switch to the Advanced tab.
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(niecreateRoot) 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-snaplub 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.
| Strategy | treść in initial HTML? | SEO risk | Best dla |
|---|---|---|---|
| CSR (raw React) | No | Highest | Logged-in panele, nie-zindeksowany apps |
| Pre-renderowanie | Yes (build time) | niski | mały, mostly-statyczny witryny |
| SSG | Yes (build time) | Lowest | Blogs, docs, marketing |
| SSR | Yes (per żądanie) | niski | Fresh, dynamiczny treść |
| ISR / hybrid | Yes | niski | treść ż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:
createRootrenders React do a DOM node z scratch — no existing znaczniki expected. używać it dla CSR-tylko apps.hydrateRootattaches React to HTML żereact-dom/serverjuż generated, i expects the client’s pierwszy render to produce output identical to co the serwer sent. If you’re SSR/SSG, you wanthydrateRoot, niecreateRoot— callingcreateRooton 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.
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.
BrowserRouterużywapushStatei produces clean, crawlable URLs (/products).HashRouterproduces/#/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
/productsdoesn’t 404. <Link>renders a rzeczywisty anchor. React Router’s<Link>component outputs an<a href>, który jest crawlable. Navigation built ononClickhandlers 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, SSRcontextserialization,onChangeClientState,prioritizeSeoTags, lubtitleTemplate. 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
metadataexport /generateMetadatain 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.
AI summary
A condensed take on the Advanced version:
- React isn’t bad dla SEO — CSR by domyślny jest. React the library obsługuje client,
serwer, statyczny, i streaming renderowanie; it’s the domyślny CRA/Vite toolchain (no
serwer) że ships an empty
<div id="root">shell i builds the DOM in the przeglądarka, so the raw HTML a crawler fetches ma no treść. - Google może render React via the Web renderowanie Service (evergreen Chromium), ale renderowanie jest queued osobno, może być delayed, i jest stateless (no cookies/localStorage/sessionStorage między loads).
- The render-timeout trap: if main treść loads slowly, renderowanie times out i Google indexes a boilerplate-tylko strona. w całym wiele URLs tamte look identical i get flagged as duplicates (Gary Illyes’s udokumentowany awaria mode). Fix: load treść pierwszy — lub better, don’t depend on the render krok (SSR/SSG).
- renderowanie strategies, best→worst dla SEO: SSG (lowest risk) ≈ SSR ≈ pre-render > ISR/hybrid > CSR (highest risk). dynamiczny renderowanie jest deprecated — Google recommends SSR, statyczny renderowanie, lub hydration.
- No ranking bonus dla SSR — Mueller: “no SEO ranking bonuses dla implementing it one way lub another.” It just makes treść reliably indeksowalny.
- Hydration jest a bug granica, nie a technique: SSR/SSG apps hydrate z
hydrateRoot(niecreateRoot), który expects the client’s pierwszy render to match the serwer’s exactly. React warns on mismatches in dev ale doesn’t guarantee patching them — treat mismatches as bugs i verify serwer vs. post-hydration DOM directly. - React Router: używać History API (
BrowserRouter), nie hash routing; the serwer musi handle tamte URLs;<Link>renders crawlable<a href>—onClick-tylko nav doesn’t. - Metadata: React 19 natively hoists
<title>/<meta>/<link>to<head>dla standalone apps needing tylko the basics. On React 18, lub dla advanced needs (SSR context,titleTemplate), używać react-helmet-async — nigdy the original react-helmet, unmaintained since 2020. In następny.js, użyj Metadata API regardless of React version. HTML-level beats JS-injected. - AI-crawler renderowanie jest provider-specific — CSR React depends on client execution że każdy crawler może lub może nie obsługiwać. SSR/SSG puts treść in the initial HTML i maximizes coverage.
- Test z View źródło vs. Inspect, URL Inspection (wyrenderowany HTML + screenshot + console), wyniki z elementami rozszerzonymi Test, JS-disabled reload, i a JS-renderowanie crawler.
- następny.js / Remix są the practical fix — they move treść do the initial HTML.
Official documentation
Primary-źródło documentation z the wyszukiwarki.
- Understand SEO JavaScript Basics — the crawl → render → index pipeline, SPAs, the History API, canonical URLs z JS, i meaningful HTTP kody stanu.
- Fix Search-powiązany JavaScript Problems — soft-404 handling in SPAs, the stateless renderer (no cookies/localStorage), i testing z URL Inspection.
- dynamiczny renderowanie (deprecated workaround) — why Google deprecated it i co to używać instead (SSR, statyczny renderowanie, hydration).
- Introducing a new SEO JavaScript video series — Martin Splitt’s series, covering React, Angular, i Vue specifically.
- In-Depth poradnik to How Google Search działa — gdzie renderowanie sits in crawl → index → serve.
Bing / Microsoft
- The new evergreen Bingbot (Microsoft Edge) — Bingbot renderowanie JavaScript via the same Chromium platforma as Googlebot.
- bingbot Series: JavaScript, dynamiczny renderowanie, i Cloaking — Bing’s older (2018) take; użyteczny dla the historical dynamiczny-renderowanie recommendation.
Technical reference
- React v19 (release notes) — native obsługiwać dla renderowanie
<title>,<meta>, i<link>znaczniki in components, hoisted automatically to<head>. - createRoot / hydrateRoot (React docs) — the client-render vs. hydration API split, i the caveat że mismatches aren’t guaranteed to być patched.
- react-helmet-async (npm) — the maintained fork dla managing
<head>in standalone React apps, React 18 lub advanced React 19 needs.
cytaty z the źródło
On-the-record statements z Google’s Search zespół. każdy search-engine deep link jumps to the quoted passage on the źródło strona; the staff cytaty below są linked to the coverage że reproduced them.
Google docs — SPAs i dynamiczny renderowanie
- “Single-page applications (SPA) are websites that load an HTML document once and fetch any additional content using JavaScript APIs.” — Google Search Central docs. Jump to cytat
- “Dynamic rendering was a workaround and not a long-term solution for problems with JavaScript-generated content in search engines.” — Google Search Central docs. Jump to cytat
Martin Splitt, Google — how JavaScript strony są zindeksowany
- “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.” przeczytaj coverage (wyszukiwarka Journal)
- On the two-waves model będąc imprecise: “there’s no such thing as the second wave of crawling-ish. The wave is an oversimplification.” przeczytaj coverage (wyszukiwarka Roundtable)
Gary Illyes, Google — the render-timeout duplicate-treść 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.” przeczytaj post (LinkedIn)
John Mueller, Google — no ranking bonus dla renderowanie 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).” przeczytaj coverage (wyszukiwarka Roundtable)
React SEO checklist
A pass to confirm crawlers może see i index twój React app:
- ważny treść appears in View źródło (raw HTML), nie tylko in the wyrenderowany DOM — if it’s missing, you’re depending on CSR.
- strony że need to rank używać SSR lub SSG (następny.js, Remix, lub a pre-render krok), nie raw client-side renderowanie.
- Main treść loads fast i pierwszy — no slow API waterfalls że mógł trip the render timeout do a boilerplate-tylko strona.
- Routing używa the History API (
BrowserRouter), nieHashRouter/#/URLs. - The serwer może respond to każdy client-side route (no 404 on bezpośredni hit lub refresh).
- Navigation używa rzeczywisty
<a href>links (React Router<Link>), nieonClick-tylko handlers on<div>/<button>. - każdy route ustawia a unique
<title>, opis meta, canonical, i OG znaczniki że update on navigation. - Metadata matches twój version: React 19 native
<title>/<meta>/<link>znaczniki dla the basics, react-helmet-async dla React 18 lub advanced needs (nigdy the deprecatedreact-helmet), lub the następny.js Metadata API if on następny.js. - If serwer-wyrenderowany, you’re hydrating z
hydrateRoot(niecreateRoot), i dowolny dev-mode hydration mismatch ostrzeżenie jest treated as a bug to fix. - Client-side “not found” routes zwracać a rzeczywisty 404 (lub a
noindex), nie a soft-404 z a200status. - JavaScript i CSS są nie blocked in
robots.txt(Google won’t render z blocked files). - No critical treść depends on cookies / localStorage / sessionStorage (the renderer jest stateless).
- Verified in URL Inspection: the wyrenderowany HTML i screenshot pokazywać twój rzeczywisty treść.
- Coverage raport checked dla “Discovered, currently not indexed” (render backlog) i duplicate clusters (render-timeout trap).
The mental modele
1. The tylko question że matters: co’s in the raw HTML? View źródło przed dowolny JavaScript runs jest co a pierwszy-fetch crawler — i najbardziej AI crawlers, forever — see. If twój treść isn’t there, you mieć a React SEO problem regardless of how good the strona looks in twój przeglądarka.
2. renderowanie jest a oddzielny, fallible krok. Crawl → render → index. CSR puts 100% of twój treść on the far side of the render krok, który jest queued, delayed, stateless, i może time out. SSR/SSG move twój treść przed że krok. Don’t ponad-trust the “two waves” model — Splitt himself called it an oversimplification.
3. The boilerplate-duplicate awaria mode. Slow treść + render timeout = każdy URL renders as header/nav/footer tylko = Google sees duplicates. The fix jest structural: load treść pierwszy, lub stop depending on the render krok.
4. The renderowanie decision tree.
- Public treść że musi rank lub być cited by AI → SSG (statyczny) lub SSR (fresh).
- Mostly statyczny (blog, docs, marketing) → SSG, lub ISR on a timer.
- Frequently changing, musi być fresh → SSR.
- Logged-in panel, nie meant to być zindeksowany → CSR jest fine.
- New build że needs SEO → reach dla następny.js / Remix, nie dynamiczny renderowanie.
5. HTML-pierwszy, JS-second dla każdy SEO signal. treść, links, canonicals, tytuły, dane strukturalne — get them do the serwer-wyrenderowany HTML. Treat JS-injected SEO signals (w tym JS canonical znaczniki i react-helmet on CSR) as a fallback, nie the plan: Google sees them late, i AI crawlers nie at wszystkie.
React SEO — cheat sheet
renderowanie modes at a glance
| Mode | treść in initial HTML? | SEO risk | używać dla |
|---|---|---|---|
| CSR (raw React) | No | Highest | Logged-in panele, nie-zindeksowany apps |
| Pre-renderowanie (react-snap) | Yes (build time) | niski | mały, mostly-statyczny witryny |
| SSG | Yes (build time) | Lowest | Blogs, docs, marketing |
| SSR | Yes (per żądanie) | niski | Fresh, dynamiczny treść |
| ISR / hybrid (następny.js) | Yes | niski | Hourly/daily treść |
| dynamiczny renderowanie | Bot-tylko | Deprecated | Don’t — używać SSR/SSG/hydration |
Common React SEO mistakes → fixes
| Mistake | Fix |
|---|---|
| treść tylko in wyrenderowany DOM (CSR) | SSR / SSG / pre-render |
Hash URLs (/#/path) | History API (BrowserRouter) |
onClick navigation, no anchor | rzeczywisty <a href> / React Router <Link> |
| Meta znaczniki don’t update on route | React 19: native <title>/<meta>/<link>. Older/advanced: react-helmet-async. następny.js: Metadata API |
react-helmet (original, dowolny React version) | Switch to react-helmet-async lub React 19 native znaczniki |
createRoot używany on serwer-wyrenderowany HTML | używać hydrateRoot instead — createRoot discards the serwer znaczniki |
| Hydration mismatch ostrzeżenie suppressed | Treat it as a bug i fix the serwer/client difference |
| Slow treść → render timeout → dups | Load main treść pierwszy; move to SSR/SSG |
Client-side 404 zwroty 200 | rzeczywisty 404 status lub noindex |
Blocked .js / .css in robots.txt | pozwalać them — Google won’t render blocked files |
| treść gated on cookies/localStorage | Don’t — the renderer jest stateless |
Fast reguły
- The renderer jest evergreen Chromium, queued, stateless, i times out.
- No ranking bonus dla SSR — it’s o niezawodny indexability (Mueller).
- AI-crawler renderowanie varies by provider → raw HTML jest the safest coverage baseline.
- In następny.js, użyj Metadata API — nie React Helmet.
- Bing renders JS (via Edge) ale mniej reliably; SSR/SSG jest the bezpieczny call there too.
sprawdzenie co the serwer wysyła przed React runs
Put representative indeksowalny routes in urls.txt. ten finds CSR shells i missing
serwer-wyrenderowany head znaczniki in the raw odpowiedź:
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.txtmały byte size jest tylko a sprawdzenie signal, nie an błąd by itself. porównywać flagged raw odpowiedzi z wyrenderowany HTML i confirm że primary tekst i crawlable links są present.
narzędzia dla auditing React SEO
- View źródło vs. Inspect element — the fastest pierwszy sprawdzenie. View źródło jest the raw HTML (co a crawler gets przed JS); Inspect element jest the wyrenderowany DOM. treść in Inspect ale nie View źródło jest JavaScript-dependent.
- URL Inspection (Google Search Console) — the źródło of truth. Run a live test, then view the wyrenderowany HTML, the screenshot, the strona zasoby (co załadowany vs. był blocked), i console messages to see exactly co Google wyrenderowany.
- wyniki z elementami rozszerzonymi Test — a quick wyrenderowany-HTML i structured-data sprawdzenie dla a single URL bez verifying the witryna.
- Chrome DevTools — disable JavaScript (Command Menu → “Disable JavaScript”) i reload to inspect co an HTML-tylko fetcher receives. ten jest a coverage sprawdzenie, nie proof of dowolny named AI crawler’s current renderowanie behavior.
- Search Console Coverage raport — watch “Discovered, currently not indexed” (render backlog) i duplicate clusters (the render-timeout boilerplate trap).
- JS-renderowanie crawlers — Ahrefs witryna Audit i Screaming Frog SEO Spider (JS-renderowanie mode) execute JavaScript so you może diff raw vs. wyrenderowany HTML w całym the witryna.
Mistakes React zespoły actually make
Concrete patterns I zachować seeing in CSR React apps że get shipped, nie hypothetical ones. każdy jest a prevention move — catch it przed it costs you indeksowanie.
wysyłka raw CRA/Vite CSR dla strony że need to rank
zespoły ship Create React App lub Vite + React straight to production dla marketing strony,
blog posts, lub strony produktów — the dokładny treść że needs to pokazywać up in search.
Why it’s błędny: the serwer wysyła an almost-empty <div id="root"> shell; twój rzeczywisty
treść tylko exists po JavaScript runs, so it’s delayed by Google’s render queue i
może być invisible to dowolny AI crawler że fetches initial HTML bez client execution.
co to robić instead: move
anything że needs to rank lub być cited to SSR lub SSG — następny.js lub Remix są the easiest
paths — i reserve raw CSR dla logged-in, non-zindeksowany surfaces like panele.
Routing on hash URLs (HashRouter)
Reaching dla React Router’s HashRouter ponieważ it’s the path of least resistance —
no serwer config needed, działa on dowolny statyczny host. Why it’s błędny: Google może’t
reliably resolve /#/products-style URLs; the old AJAX-crawling scheme że made hash
fragments crawlable jest deprecated. co to robić instead: używać BrowserRouter (the
History API) i upewnij się the serwer responds to każdy route it produces, w tym a
bezpośredni hit lub refresh on a deep link.
Building navigation on onClick zamiast rzeczywisty anchors
Wiring up navigation z onClick handlers on a <div> lub <button>, często ponieważ it
był easier to style lub avoid domyślny link behavior. Why it’s błędny: Google tylko
follows rzeczywisty <a href> links — a <div> z a click handler jest invisible to crawling,
no matter how it behaves dla a mouse. co to robić instead: używać React Router’s
<Link> component, który renders an rzeczywisty <a href> poniżej the hood, lub a plain anchor
znacznik dla external navigation.
ładowanie main treść behind a slow API waterfall
pobieranie the header i nav fast, then chaining several API calls przed the rzeczywisty strona treść — the part że makes każdy URL unique — appears. Why it’s błędny: Google’s Web renderowanie Service enforces a timeout; if twój centerpiece treść loads slowly, renderowanie finishes przed it arrives, i Google jest left indeksowanie boilerplate-tylko strony że then get flagged as duplicates of każdy other — the dokładny awaria mode Gary Illyes described. co to robić instead: restructure żądania so main treść loads pierwszy, lub usuń dependency on client-side renderowanie entirely z SSR/SSG.
nadal używając the original react-helmet
Reaching dla react-helmet dla per-route <title> i meta znaczniki ponieważ it’s the
library każdy older tutorial recommends. Why it’s błędny: the original package jest
unmaintained — no release since 2020 — i ma znany problemy poniżej React 18’s concurrent
renderowanie i SSR. co to robić instead: on React 19, render <title>/<meta>/<link>
directly in twój components i let React hoist them (no library needed dla the basics).
On React 18, lub dla advanced needs like SSR context serialization lub titleTemplate, używać
react-helmet-async, the maintained fork. On następny.js, używać jego built-in Metadata API i
don’t bolt Helmet on top of it.
Blocking JavaScript lub CSS in robots.txt
Blocking /static/js/ lub a bundler’s asset folder in robots.txt, czasami left ponad
z an old crawl-budget concern lub copied z another witryna’s config. Why it’s
błędny: Google może’t render co it isn’t allowed to fetch — a blocked bundle means the
Web renderowanie Service builds an incomplete (lub empty) DOM, even though twój kod źródłowy
jest fine. co to robić instead: pozwalać crawlers to fetch twój JS i CSS, i confirm it
z the URL Inspection narzędzie’s strona-zasoby sprawdzenie to see nothing critical jest blocked.
Test yourself: React SEO
Five quick questions on making React apps crawlable i indeksowalny. Pick an answer dla każdy, then sprawdzenie.
zasoby worth twój time
My powiązany writing
- React SEO: dobre praktyki to Make It SEO-Friendly (Ahrefs) — the React SEO poradnik on Ahrefs, który I sprawdzony; ten artykuł jest the deeper, źródło-linked treatment.
- SEO JavaScript: A Definitive poradnik (Ahrefs) — my pełny poradnik to the underlying renderowanie mechanics: DOM parity, the najbardziej-restrictive-directive reguła, canonical i meta-znacznik handling, i renderowanie choices. Read ten dla the general case behind React-specific fixes.
- The Beginner’s poradnik to techniczne SEO (Ahrefs) — gdzie React/SEO JavaScript fits in the bigger picture.
My speaking
- How Search działa (SlideShare) — my walkthrough of crawling, renderowanie, indeksowanie, i ranking. (My standing disclaimer applies: “This is my understanding of systems… not going to be 100% complete or accurate.”)
z around the industry
- Understand SEO JavaScript Basics (Google) — primary-źródło docs on SPAs, the History API, i canonical/status-code handling.
- dynamiczny renderowanie (deprecated) (Google) — why dynamiczny renderowanie jest a workaround, nie a long-term solution.
- Martin Splitt wyjaśnia How JavaScript witryny są zindeksowany (wyszukiwarka Journal) — the “barebone HTML … boom” explanation of the crawl → render → index flow.
- Gary Illyes on JS-heavy witryny i duplikat treści (LinkedIn) — the render-timeout-to-duplicates awaria mode, in his own words.
- How to fix techniczne SEO problemy on client-side React apps (wyszukiwarka Land) — a rzeczywisty-world case study on auditing i fixing a CSR React app.
- SSR vs. dynamiczny renderowanie — no ranking difference (wyszukiwarka Roundtable) — Mueller’s “no SEO ranking bonuses” statement.
- react-helmet-async (npm) — the maintained head-management library dla standalone React.
- The new evergreen Bingbot (Microsoft Edge) (Bing) — Bingbot renderowanie JS via Chromium, like Googlebot.
Videos
- Google Search Central — SEO JavaScript series (YouTube) — Martin Splitt’s official video series covers SEO dla React, Angular, i Vue specifically, walking przez the crawl → render → index proces i the common fixes. Series announcement · Channel
Dziennik zmian
Zaktualizowano 18 lip 2026.
Podsumowanie redakcyjne i zapisane szczegóły zmian.Szczegóły zmian
-
Szczegółowe uwagi dotyczące zmian są obecnie dostępne po angielsku.
-
Szczegółowe uwagi dotyczące zmian są obecnie dostępne po angielsku.
-
Szczegółowe uwagi dotyczące zmian są obecnie dostępne po angielsku.
Pełne porównanie jest niedostępne — dla tej wersji nie zarchiwizowano wcześniejszej migawki.