React SEO
React renders client-side by default, so crawlers see an empty shell until JavaScript runs. Here's how Google actually processes React apps, which rendering strategy to pick, and how to fix routing, metadata, and the render-timeout duplicate-content trap.
React is not bad for SEO — but client-side rendering by default is. Out of the box (CRA, Vite + React), the server ships an empty shell and the browser builds the page, so crawlers see nothing until JavaScript runs. Google can render React via its Web Rendering Service, but rendering is queued, delayed, and can time out — Gary Illyes has shown render timeouts leaving boilerplate-only pages that get 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 (most easily Next.js or Remix) puts content in the initial HTML, hydrated with hydrateRoot (not createRoot) so server and client output match exactly. Then use History API routing, real <a href> links, correct status codes, and version-appropriate metadata: React 19 hoists <title>/<meta>/<link> natively, otherwise react-helmet-async (never the unmaintained original react-helmet).
TL;DR — React is not bad for SEO — but the way most React apps are built is. By default, React builds the page in the visitor’s browser, so when a search engine first fetches your URL it gets a nearly empty page. Google can usually fill in the blanks by running your JavaScript, but it’s slower and riskier than just handing it finished HTML. The fix is to render your pages on a server or at build time — usually with a framework like Next.js.
Why React is different
Most websites — a WordPress blog, say — send the search engine a complete page:
the server builds the HTML and ships it, headline and all. A standard React app does
the opposite. The server sends an almost-empty shell (basically an empty <div>),
and then JavaScript runs in the browser to build the real page.
That’s great for slick, app-like experiences. It’s a problem for SEO, because the first thing a crawlerA crawler — also called a spider or bot — is an automated program that fetches web pages, extracts their links, and queues new URLs to visit. Search engines use crawlers to discover and download content for their index. downloads is that empty shell. Your content isn’t there yet — it only shows up after the JavaScript runs.
Can’t Google just run the JavaScript?
Yes — Google runs a real, up-to-date version of Chrome behind the scenes and can execute your JavaScript to see the finished page. So React content can get indexedStoring a crawled page in the search index so it can appear in results. Crawled is not the same as indexed — Google selects what to keep, and indexing isn't guaranteed.. 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 there are catches:
- It’s delayed. Google does the renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. later, in a separate step that’s queued up. So your content can take longer to show up in search.
- It can fail. If your page is slow to load its content, Google’s renderer can give up before the content appears — and index a blank-ish page.
- Other crawlersA crawler — also called a spider or bot — is an automated program that fetches web pages, extracts their links, and queues new URLs to visit. Search engines use crawlers to discover and download content for their index. vary. Bing handles JavaScript less reliably, and AI providers do not publish one shared renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. contract. Any crawler that fetches only the initial HTML will see a default React app as empty.
The simple fix
Get your content into the HTML before it reaches the browser. Two ways:
- Server-side rendering (SSR) — a server builds the full page for each request.
- Static site generation (SSG) — pages are built into 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 is Next.js, a framework built on React that does this for you. (Remix is another good option.) With SSR or SSG, your React site hands crawlers a complete page — and it’s as search-friendly as any normal website.
A few other things to get right
- Use normal-looking URLs (
/products), not hash URLs (/#/products) — Google can’t reliably index the hash ones. - Make your links real links (
<a href>), not clickable<div>s. - Give each page its own title and description that update when the page changes.
Want the deeper version — how Google’s renderer actually works, the render-timeout trap that creates duplicate pagesThe same or very similar primary content reachable at more than one URL. There's no general duplicate content penalty — the real costs are possible signal dilution, the wrong URL getting chosen, and less-efficient crawling., the rendering-strategy comparison, and how to test what Google sees? Switch to the Advanced tab.
TL;DR — React’s SEO problem isn’t React — it’s client-side renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. by default. CRA and Vite + React ship an empty shell and build the DOM in the browser, so the raw HTML a crawlerA crawler — also called a spider or bot — is an automated program that fetches web pages, extracts their links, and queues new URLs to visit. Search engines use crawlers to discover and download content for their index. fetches has no content. Google can render it via the Web RenderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. Service (evergreen Chromium), but rendering is queued separately, can be delayed, and can time out — Gary Illyes has documented render timeouts leaving boilerplate-only pages that then get flagged as duplicates. Bing renders JS less reliably; AI-crawlerA crawler — also called a spider or bot — is an automated program that fetches web pages, extracts their links, and queues new URLs to visit. Search engines use crawlers to discover and download content for their index. rendering varies by provider. The fix is rendering strategy: SSR or SSG (most easily Next.js or Remix) puts content in the initial HTML — and if you’re hydrating server-rendered markup, use
hydrateRoot(notcreateRoot) and treat any server/client mismatch as a bug, not a warning to suppress. Then use History API routing (not hash URLs) and real<a href>links. For<head>metadata: React 19 hoists<title>/<meta>/<link>natively; on React 18 or for advanced needs, use react-helmet-async (never the unmaintained original react-helmet). Set correct HTTP status codes. There’s no ranking bonus for SSR — it just makes content reliably indexable. For the general rendering mechanics, see the JavaScript SEOMaking sure search engines can crawl, render, and index content that depends on JavaScript. and headless CMSA content management system (CMS) is software that lets users create, manage, and publish digital content — like blog posts and pages — without writing raw code. WordPress, Drupal, and Joomla are the most common open-source CMS platforms. topics.
What actually makes React hard for SEO
React is a component-based JavaScript library, and out of the box — Create React App,
Vite + React — it runs client-side. The server returns a near-empty document
(famously just a <div id="root"></div>) plus a bundle of JavaScript, and the browser
executes that JavaScript to construct the DOM. Contrast that with a server-rendered
page (WordPress, a Rails app), where the full HTML — content, headings, links —
arrives in the very first response.
So the question that decides everything is: what’s in the raw HTML before any JavaScript runs? For a default React app, the answer is “almost nothing.” Right-click → View Source on a CRA app and you’ll see the shell, not the content. That’s exactly what a crawler gets on its first fetch.
To be precise about where the responsibility actually sits: React the library isn’t
CSR-only. React DOM ships client rendering (createRoot), server rendering (streaming
and static APIs), and hydrationActivating server-rendered HTML in the browser by attaching JavaScript handlers. APIs — the library supports all of it. The empty-shell
problem is a property of the default toolchain (Create React App, Vite + React with
no server), which wires up only the client APIs and nothing that renders to HTML on the
server. Swap the toolchain — Next.js, Remix, or React’s own server-rendering APIs — and
the same library ships full HTML on the first response.
This is the React-specific application of the broader JavaScript SEOMaking sure search engines can crawl, render, and index content that depends on JavaScript. problem — go there for the general failure modes (parity, interaction, state, timing). Here I’ll focus on what’s specific to React and how to fix it.
How Google actually processes a React app
Google handles JavaScript in three phases: crawl → render → indexStoring a crawled page in the search index so it can appear in results. Crawled is not the same as indexed — Google selects what to keep, and indexing isn't guaranteed.. GooglebotGooglebot is Google's web crawler — the software that fetches pages so Google can index and rank them. It comes in two variants, Googlebot Smartphone (primary, under mobile-first indexing) and Googlebot Desktop, and runs an evergreen Chromium renderer. fetches the URL, the rendered DOM is built later by the Web Rendering Service (WRS) — an evergreen version of Chromium, the same engine as Chrome — and then the rendered output gets indexed and its 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 important nuance is when rendering happens. Rendering is resource-intensive, so it’s queued separately from the initial crawl. Martin Splitt described the flow plainly: “we do an HTTP request, and we get something back … some barebone HTML and all it does is load the JavaScript and run the JavaScript. Then, this HTML … goes into rendering. Rendering runs JavaScript — boom!, a lot of content happens that wasn’t there before.” For a CSR React page, the “boom” is your entire page — none of it exists until that render step runs.
A caveat worth carrying: don’t over-index on the old “two waves of indexingStoring a crawled page in the search index so it can appear in results. Crawled is not the same as indexed — Google selects what to keep, and indexing isn't guaranteed.” 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 that rendering is a distinct, deferrable, fallible step, and CSR puts 100% of your content on the wrong side of it.
Two more facts about the renderer that bite React apps specifically:
- It’s stateless. GooglebotGooglebot is Google's web crawler — the software that fetches pages so Google can index and rank them. It comes in two variants, Googlebot Smartphone (primary, under mobile-first indexing) and Googlebot Desktop, and runs an evergreen Chromium renderer. does not retain
localStorage,sessionStorage, or cookies between page loads. Any content or routing that depends on client-side state is invisible to the crawler. - It can give up. The renderer enforces a timeout. If your main content loads slowly — large bundles, waterfalls of API calls — rendering can finish before your content arrives, and Google indexes the incomplete page.
The render-timeout trap (and why it creates duplicates)
This failure mode is rarely explained well, and it’s the most damaging one for React apps. Gary Illyes described it directly: “I have a bunch of emails in my inbox where the issue is that the centerpiece took forever to load, so rendering timed out … and we were left with a bunch of pages that only had the boilerplate. With only the boilerplate, those pages are dups.”
Walk through what that means for a CSR React app. Your header, nav, and footer are boilerplate that loads fast. Your actual page content — the part that makes each URL unique — is fetched and rendered by JavaScript, and it loads slowly. Rendering times out. Google is left with header + nav + footer on every URL. Now every page looks identical, and Google marks them as duplicates of each other in Search ConsoleA free Google service that reports how a site performs in Google Search and surfaces problems with how Google crawls, indexes, and serves it. It's first-party data straight from Google — but you don't need it to appear in results..
Illyes’s own fix is the actionable bit: “Try to restructure the js calls such that the content (including marginal boilerplate) loads first and see if that helps.” But the more durable answer is to not depend on the render step for your main content at all — which means SSR or SSG.
Rendering strategies for React
This is the single most impactful decision. The options, roughly worst to best for SEO:
- CSR (default React). Server sends the shell; browser builds everything. Content is delayed by the render queue and exposed to the timeout. Worst for SEO. Fine for authenticated dashboards you don’t want indexed 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 without a full SSR framework — tools like
react-snapor a prerenderThe Speculation Rules API is a Chromium browser API (Chrome/Edge 109+) that lets a site tell the browser which same-site pages to prefetch (download the HTML document) or prerender (fully load and render in an invisible tab) before a visitor clicks — so the next navigation can be near-instant. It's a browser-side performance feature for real users, not a crawling, indexing, or ranking signal. service crawl your app and save static HTML. Lighter-weight; works for simpler, mostly-static sites. - SSG (Static Site Generation). HTML built once at deploy time and served as static files. Fastest, content always present in the raw HTML. Limited for highly dynamic or per-user content; large sites get slow builds.
- SSR (Server-Side Rendering). The server executes React per request and sends full HTML. Content immediately available to crawlers; always fresh. Costs a Node.js server and slightly higher TTFBTime to First Byte — the time from the start of a request to when the first byte of the response arrives. It's a diagnostic metric (not a Core Web Vital) and a major input to FCP and LCP; ≤0.8 s is good..
- Hybrid / ISR (Incremental Static Regeneration). A Next.js feature that regenerates static pages in the background — static speed with periodic freshness.
| Strategy | Content in initial HTML? | SEO risk | Best for |
|---|---|---|---|
| CSR (raw React) | No | Highest | Logged-in dashboards, not-indexed apps |
| Pre-rendering | Yes (build time) | Low | Small, mostly-static sites |
| SSG | Yes (build time) | Lowest | Blogs, docs, marketing |
| SSR | Yes (per request) | Low | Fresh, dynamic content |
| ISR / hybrid | Yes | Low | Content that changes hourly/daily |
And one strategy to skip on new builds: dynamic rendering — detecting the crawler user-agent and serving it a pre-rendered version while users get CSR. Google now calls it “a workaround and not a long-term solution” that “creates additional complexities and resource requirements,” and recommends server-side rendering, static rendering, or hydration instead. (Bing recommended dynamic rendering back in 2018, but that guidance is dated — since 2019 BingbotBingbot is Microsoft Bing's primary web crawler — the bot that discovers, fetches, and renders pages to build the Bing index. That index also powers Yahoo, DuckDuckGo, Ecosia, and Microsoft Copilot, so Bingbot's reach is far wider than Bing's own search-market share. renders via Microsoft Edge / Chromium, and SSR/SSG is the right call there too.)
There’s a myth worth killing here: SSR is not a ranking boost. As John Mueller put it, “there are no SEO ranking bonuses for implementing it one way or another” — the different rendering methods are “just different ways of making the content indexable.” SSR’s value is reliable indexability (and often better Core Web VitalsGoogle's three real-user UX metrics — LCP (loading), INP (responsiveness), and CLS (visual stability) — used by Google's ranking systems, with no official weight attached, measured on field data. from a faster First Contentful Paint), not a magic ranking lever.
Hydration must match exactly — that’s a bug boundary, not an SEO technique
SSR and SSG both hand the browser HTML that already has your content in it. React then has to attach to that markup on the client, and that’s a different API from a plain client render:
createRootrenders React into a DOM node from scratch — no existing markup expected. Use it for CSR-only apps.hydrateRootattaches React to HTML thatreact-dom/serveralready generated, and expects the client’s first render to produce output identical to what the server sent. If you’re SSR/SSG, you wanthydrateRoot, notcreateRoot— callingcreateRooton server-rendered markup means React discards it and re-renders from scratch, throwing away the exact SEO benefit you set up SSR/SSG to get.
Mismatches between server and client output are a real 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 own docs are blunt about what
happens then: it warns about mismatches in development, but “there are no guarantees
that attribute differences will be patched up in case of mismatches.” The guidance is
to treat mismatches as bugs and fix them — not to suppress the warning and assume
content parity. For SEO specifically: don’t assume your rendered content and metadata
match what the server sent just because the page looks right in the browser. Diff the
server HTML against the post-hydration DOM directly (the View Source vs. Inspect
Element check from the testing section below is the fast version of this) rather than
trusting a clean console.
React Router and URL structure
React Router handles navigation in the browser without server round-trips, which is fine for SEO if configured correctly:
- Use the History API, not hash routing.
BrowserRouterusespushStateand produces clean, crawlable URLs (/products).HashRouterproduces/#/products, and Google cannot reliably resolve hash-based URLs — the old AJAX-crawlingCrawling is how search engines use automated bots (like Googlebot and Bingbot) to discover URLs and download pages. A page has to be crawlable to be indexed, but crawling on its own isn't a ranking factor. scheme that made them work is deprecated. Use the History API. - The server must handle those URLs too. With History API routing, every “page” needs
a real URL the server can respond to — critical for SSR, and necessary so a direct hit
or refresh on
/productsdoesn’t 404. <Link>renders a real anchor. React Router’s<Link>component outputs an<a href>, which is crawlable. Navigation built ononClickhandlers without an anchor is not crawlable — Google only follows real<a href>links.
Managing metadata: react-helmet, react-helmet-async, and React 19’s native tags
Through React 18, React never updated the document <head> on route changes natively —
every route’s <title>, meta descriptionThe meta description is an HTML head tag — `<meta name=\"description\" content=\"…\">` — that suggests a short summary of the page for the search snippet. It's not a Google ranking factor, and Google rewrites it the majority of the time, but a good one can still lift click-through., canonical, and Open GraphOpen Graph (OG) tags are `<meta>` elements in a page's head, defined by the Open Graph protocol (ogp.me, created by Facebook), that describe a page as a shareable object — its title, description, image, URL, and type. They control how a link preview card looks when the page is shared on Facebook, LinkedIn, Slack, Discord, WhatsApp, and iMessage. They are not a direct Google ranking factor, though Google reads og:title, og:image, and og:site_name as inputs to how a result appears. / Twitter tags had
to be set by a library. React 19 changed that: components can render <title>,
<meta>, and <link> tags directly, and React hoists them to <head> on its own —
working with client-only apps, streaming SSR, and Server Components alike. React 19.2 is
the current stable release as of mid-2026, so this now applies to any app on a current
React version.
That means the right answer depends on your React version and what you actually need:
- React 19, standalone app, only basic tags. Render
<title>/<meta>/<link>in your components directly — no library needed. - React 19, but you need
htmlAttributes/bodyAttributes, SSRcontextserialization,onChangeClientState,prioritizeSeoTags, ortitleTemplate. Native hoisting doesn’t cover these — usereact-helmet-async. Its own docs call this out directly: without those specific needs, you may not need the package at all on React 19. - React 18 or earlier, standalone app. Native hoisting doesn’t exist yet — use
react-helmet-async. It’s actively maintained (major version 3, and it detects your React version at runtime) and supports SSR. - The original
react-helmet. Don’t use it, on any React version. It’s unmaintained — no release since 2020 — and has known bugs under React 18’s concurrent rendering. - Next.js apps. Use Next’s own 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 either. The framework owns the document in a Next.js app.
One reliability rule carries over from JavaScript SEO generally, and applies no matter which of the above you use: HTML-level metadata beats JS-injected metadata. A canonical tag injected by client-side JavaScript is far less reliable than one present in the server-rendered HTML — which is another argument for SSR/SSG.
AI crawlers make this urgent
The 2026 wrinkle: rendering behavior for GPTBot (OpenAI), ClaudeBot (Anthropic), PerplexityBot, and other agents is provider- and version-specific. A CSR React app ships every crawler the same empty shell the first Googlebot fetch sees, and current provider documentation does not establish one shared render step that will fill it in. As generative engines become a bigger discovery surface, SSR/SSG stops being a Google-only concern: raw HTML maximizes coverage without assuming a universal crawler limitation. (The headless CMSA content management system that separates the content repository from the presentation layer, delivering content via API to any front-end framework rather than rendering HTML server-side itself. It doesn't specify the rendering mode, hosting, cache, preview security, or publishing workflow — those are separate decisions. topic covers this AI-crawler reality in more depth.)
Testing what Google actually sees
Don’t trust your browser — your DevTools Inspector shows the rendered DOM (post-JavaScript), which is exactly what a crawler-without-JS does not see. Use the right tools:
- View Source vs. Inspect Element. View Source is the raw HTML (what crawlers get before JS). Inspect Element is the rendered DOM. If content is in Inspect but missing from View Source, it’s JavaScript-dependent.
- URL Inspection ToolA Google Search Console feature that reports how Google sees one specific URL on a property you own. By default it shows the last-indexed snapshot; a separate \"Test live URL\" mode fetches the current version. (Search ConsoleGoogle's free tool for monitoring crawling, indexing, and search performance.) — the most authoritative check. Run a live test and look at the rendered HTML, the screenshot, and the page resources / console messages to see what Google actually rendered and what failed to load.
- Rich ResultsRich results (formerly 'rich snippets') are enhanced search listings — stars, images, prices, breadcrumbs, video thumbnails, and more — that Google and Bing build from structured data. They're a display feature, not a ranking factor, and eligibility never guarantees they'll show. Test — a quick rendered-HTML check without verifying the site.
- Disable JavaScript in DevTools and reload — a fast simulation of a crawler that doesn’t execute JS (and a decent proxy for what AI crawlersAI crawlers are bots from AI companies that fetch web pages to train language models, build AI-search indexes, or answer live user questions. They come in three categories, each with its own user-agent tokens and its own robots.txt controls. see).
- Search Console Coverage report — “Discovered, currently not indexed” can signal a render queue backlog; clusters of duplicate pages can signal the render-timeout boilerplate trap.
- JS-rendering crawlers — Ahrefs Site Audit and Screaming Frog (JS-rendering mode) render pages at scale so you can diff raw vs. rendered across the whole site.
Next.js and Remix (the practical answer)
If SEO matters and you’re on raw CSR React, migrating to a framework that renders on the server is usually the right move. Next.js is purpose-built for this — 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, built on fetch/Request/Response with SSR by default and a strong progressive-enhancement story. Next.js gets its own deep dive — I’m keeping it brief here on purpose. The point for React SEOReact SEO is the practice of making React apps crawlable and indexable. React renders client-side by default, so the raw HTML is near-empty until JavaScript runs — SSR or SSG puts the content back in the initial response where crawlers (and AI bots) can reliably see it. is narrower: the framework exists to move your content out of the browser-only render step and into the initial HTML.
React is good for SEO when you treat rendering as an architecture decision rather than an afterthought. Pick SSR or SSG for anything that needs to rank, keep your links and routing honest, manage metadata per route, and let Google’s own tools — not your browser — tell you what actually rendered.
AI summary
A condensed take on the Advanced version:
- React isn’t bad for SEO — CSR by default is. React the library supports client,
server, static, and streaming renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM.; it’s the default CRA/Vite toolchain (no
server) that ships an empty
<div id="root">shell and builds the DOM in the browser, so the raw HTML a crawlerA crawler — also called a spider or bot — is an automated program that fetches web pages, extracts their links, and queues new URLs to visit. Search engines use crawlers to discover and download content for their index. fetches has no content. - Google can render React via the Web RenderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. Service (evergreen Chromium), but rendering is queued separately, can be delayed, and is stateless (no cookies/localStorage/sessionStorage between loads).
- The render-timeout trap: if main content loads slowly, rendering times out and Google indexes a boilerplate-only page. Across many URLs those look identical and get flagged as duplicates (Gary Illyes’s documented failure mode). Fix: load content first — or better, don’t depend on the render step (SSR/SSG).
- Rendering strategies, best→worst for SEO: SSG (lowest risk) ≈ SSR ≈ pre-render > ISR/hybrid > CSR (highest risk). Dynamic rendering is deprecated — Google recommends SSR, static rendering, or hydrationActivating server-rendered HTML in the browser by attaching JavaScript handlers..
- No ranking bonus for SSR — Mueller: “no SEO ranking bonuses for implementing it one way or another.” It just makes content reliably indexable.
- Hydration is a bug boundary, not a technique: SSR/SSG apps hydrate with
hydrateRoot(notcreateRoot), which expects the client’s first render to match the server’s exactly. React warns on mismatches in dev but doesn’t guarantee patching them — treat mismatches as bugs and verify server vs. post-hydration DOM directly. - React Router: use History API (
BrowserRouter), not hash routing; the server must handle those URLs;<Link>renders crawlable<a href>—onClick-only nav doesn’t. - Metadata: React 19 natively hoists
<title>/<meta>/<link>to<head>for standalone apps needing only the basics. On React 18, or for advanced needs (SSR context,titleTemplate), use react-helmet-async — never the original react-helmet, unmaintained since 2020. In Next.js, use the Metadata API regardless of React version. HTML-level beats JS-injected. - AI-crawlerA crawler — also called a spider or bot — is an automated program that fetches web pages, extracts their links, and queues new URLs to visit. Search engines use crawlers to discover and download content for their index. rendering is provider-specific — CSR React depends on client execution that each crawler may or may not support. SSR/SSG puts content in the initial HTML and maximizes coverage.
- Test with View Source vs. Inspect, URL InspectionA Google Search Console feature that reports how Google sees one specific URL on a property you own. By default it shows the last-indexed snapshot; a separate \"Test live URL\" mode fetches the current version. (rendered HTML + screenshot + console), Rich ResultsRich results (formerly 'rich snippets') are enhanced search listings — stars, images, prices, breadcrumbs, video thumbnails, and more — that Google and Bing build from structured data. They're a display feature, not a ranking factor, and eligibility never guarantees they'll show. Test, JS-disabled reload, and a JS-rendering crawler.
- Next.js / Remix are the practical fix — they move content into the initial HTML.
Official documentation
Primary-source documentation from the search engines.
- Understand JavaScript SEO Basics — the crawl → render → indexStoring a crawled page in the search index so it can appear in results. Crawled is not the same as indexed — Google selects what to keep, and indexing isn't guaranteed. pipeline, SPAs, the History API, canonical URLsHow search engines pick one canonical URL among duplicates and consolidate signals onto it. with JS, and meaningful HTTP status codesAn HTTP status code is the three-digit number a server returns with every response to tell a browser or crawler what happened to its request — success, redirect, client error, or server error. For SEO the code matters as much as the content: it tells Google and Bing whether to index a page, follow a redirect, retry later, or drop the URL from the index..
- Fix Search-Related JavaScript Problems — soft-404 handling in SPAs, the stateless renderer (no cookies/localStorage), and testing with URL InspectionA Google Search Console feature that reports how Google sees one specific URL on a property you own. By default it shows the last-indexed snapshot; a separate \"Test live URL\" mode fetches the current version..
- Dynamic Rendering (deprecated workaround) — why Google deprecated it and what to use instead (SSR, static renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM., hydrationActivating server-rendered HTML in the browser by attaching JavaScript handlers.).
- Introducing a new JavaScript SEO video series — Martin Splitt’s series, covering React, Angular, and Vue specifically.
- In-Depth Guide to How Google Search Works — where renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. sits in crawl → index → serve.
Bing / Microsoft
- The new evergreen Bingbot (Microsoft Edge) — BingbotBingbot is Microsoft Bing's primary web crawler — the bot that discovers, fetches, and renders pages to build the Bing index. That index also powers Yahoo, DuckDuckGo, Ecosia, and Microsoft Copilot, so Bingbot's reach is far wider than Bing's own search-market share. rendering JavaScript via the same Chromium platform as GooglebotGooglebot is Google's web crawler — the software that fetches pages so Google can index and rank them. It comes in two variants, Googlebot Smartphone (primary, under mobile-first indexing) and Googlebot Desktop, and runs an evergreen Chromium renderer..
- bingbot Series: JavaScript, Dynamic Rendering, and Cloaking — Bing’s older (2018) take; useful for the historical dynamic-rendering recommendation.
Technical reference
- React v19 (release notes) — native support for 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 that mismatches aren’t guaranteed to be patched.
- react-helmet-async (npm) — the maintained fork for managing
<head>in standalone React apps, React 18 or advanced React 19 needs.
Quotes from the source
On-the-record statements from Google’s Search team. Each search-engine deep link jumps to the quoted passage on the source page; the staff quotes below are linked to the coverage that reproduced them.
Google docs — SPAs and dynamic renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM.
- “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 quote
- “Dynamic renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. was a workaround and not a long-term solution for problems with JavaScript-generated content in search engines.” — Google Search Central docs. Jump to quote
Martin Splitt, Google — how JavaScript pages are indexedStoring a crawled page in the search index so it can appear in results. Crawled is not the same as indexed — Google selects what to keep, and indexing isn't guaranteed.
- “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.” Read the coverage (Search Engine Journal)
- On the two-waves model being imprecise: “there’s no such thing as the second wave of crawlingCrawling is how search engines use automated bots (like Googlebot and Bingbot) to discover URLs and download pages. A page has to be crawlable to be indexed, but crawling on its own isn't a ranking factor.-ish. The wave is an oversimplification.” Read the coverage (Search Engine 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 ConsoleA free Google service that reports how a site performs in Google Search and surfaces problems with how Google crawls, indexes, and serves it. It's first-party data straight from Google — but you don't need it to appear in results.? Try to restructure the js calls such that the content (including marginal boilerplate) loads first and see if that helps.” Read the post (LinkedIn)
John Mueller, Google — no ranking bonus for 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).” Read the coverage (Search Engine Roundtable)
React SEO checklist
A pass to confirm crawlersA crawler — also called a spider or bot — is an automated program that fetches web pages, extracts their links, and queues new URLs to visit. Search engines use crawlers to discover and download content for their index. can see and indexStoring a crawled page in the search index so it can appear in results. Crawled is not the same as indexed — Google selects what to keep, and indexing isn't guaranteed. your React app:
- Important content appears in View Source (raw HTML), not only in the rendered DOM — if it’s missing, you’re depending on CSR.
- Pages that need to rank use SSR or SSG (Next.js, Remix, or a pre-render step), not raw client-side renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM..
- Main content loads fast and first — no slow API waterfalls that could trip the render timeout into a boilerplate-only page.
- Routing uses the History API (
BrowserRouter), notHashRouter/#/URLs. - The server can respond to every client-side route (no 404 on direct hit or refresh).
- Navigation uses real
<a href>links (React Router<Link>), notonClick-only handlers on<div>/<button>. - Each route sets a unique
<title>, meta descriptionThe meta description is an HTML head tag — `<meta name=\"description\" content=\"…\">` — that suggests a short summary of the page for the search snippet. It's not a Google ranking factor, and Google rewrites it the majority of the time, but a good one can still lift click-through., canonical, and OG tagsOpen Graph (OG) tags are `<meta>` elements in a page's head, defined by the Open Graph protocol (ogp.me, created by Facebook), that describe a page as a shareable object — its title, description, image, URL, and type. They control how a link preview card looks when the page is shared on Facebook, LinkedIn, Slack, Discord, WhatsApp, and iMessage. They are not a direct Google ranking factor, though Google reads og:title, og:image, and og:site_name as inputs to how a result appears. that update on navigation. - Metadata matches your version: React 19 native
<title>/<meta>/<link>tags for the basics, react-helmet-async for React 18 or advanced needs (never the deprecatedreact-helmet), or the Next.js MetadataNext.js SEO is the set of practices and built-in features that make a Next.js site crawlable, indexable, and rankable — rendering mode (SSG/SSR/ISR/Server Components), the App Router Metadata API, sitemap.ts/robots.ts conventions, next/image, and next/link. API if on Next.js. - If server-rendered, you’re hydrating with
hydrateRoot(notcreateRoot), and any dev-mode hydrationActivating server-rendered HTML in the browser by attaching JavaScript handlers. mismatch warning is treated as a bug to fix. - Client-side “not foundA 302 (\"Found\") is a temporary redirect: it forwards users to a new URL while telling search engines the original URL should stay in the index. It's a weak canonicalization signal, not the zero-equity dead end of SEO folklore.” routes return a real 404 (or a
noindex), not a soft-404 with a200status. - JavaScript and CSS are not blocked in
robots.txt(Google won’t render from blocked files). - No critical content depends on cookies / localStorage / sessionStorage (the renderer is stateless).
- Verified in URL InspectionA Google Search Console feature that reports how Google sees one specific URL on a property you own. By default it shows the last-indexed snapshot; a separate \"Test live URL\" mode fetches the current version.: the rendered HTML and screenshot show your real content.
- Coverage report checked for “Discovered, currently not indexed” (render backlog) and duplicate clusters (render-timeout trap).
The mental models
1. The only question that matters: what’s in the raw HTML? View Source before any JavaScript runs is what a first-fetch crawlerA crawler — also called a spider or bot — is an automated program that fetches web pages, extracts their links, and queues new URLs to visit. Search engines use crawlers to discover and download content for their index. — and most AI crawlersAI crawlers are bots from AI companies that fetch web pages to train language models, build AI-search indexes, or answer live user questions. They come in three categories, each with its own user-agent tokens and its own robots.txt controls., forever — see. If your content isn’t there, you have a React SEOReact SEO is the practice of making React apps crawlable and indexable. React renders client-side by default, so the raw HTML is near-empty until JavaScript runs — SSR or SSG puts the content back in the initial response where crawlers (and AI bots) can reliably see it. problem regardless of how good the page looks in your browser.
2. RenderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. is a separate, fallible step. Crawl → render → indexStoring a crawled page in the search index so it can appear in results. Crawled is not the same as indexed — Google selects what to keep, and indexing isn't guaranteed.. CSR puts 100% of your content on the far side of the render step, which is queued, delayed, stateless, and can time out. SSR/SSG move your content before that step. Don’t over-trust the “two waves” model — Splitt himself called it an oversimplification.
3. The boilerplate-duplicate failure mode. Slow content + render timeout = every URL renders as header/nav/footer only = Google sees duplicates. The fix is structural: load content first, or stop depending on the render step.
4. The renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. decision tree.
- Public content that must rank or be cited by AIAn AI citation is the visible source link an AI answer engine shows next to its generated text — the clickable reference that credits the web page it used. A citation's presence is a separate thing from whether the cited page actually supports the statement, and from being retrieved (read behind the scenes) or merely mentioned (named without a link); citation is driven more by brand mentions and being retrievable than by traditional ranking. → SSG (static) or SSR (fresh).
- Mostly static (blog, docs, marketing) → SSG, or ISR on a timer.
- Frequently changing, must be fresh → SSR.
- Logged-in dashboard, not meant to be indexed → CSR is fine.
- New build that needs SEO → reach for Next.js / Remix, not dynamic rendering.
5. HTML-first, JS-second for every SEO signal. Content, links, canonicals, titles, structured dataStructured data is a standardized way of labeling page content (using the schema.org vocabulary in JSON-LD, Microdata, or RDFa) so search engines can understand its meaning. It's not a direct ranking factor — its value is rich results and entity understanding. — get them into the server-rendered HTML. Treat JS-injected SEO signals (including JS canonical tagsA rel=\"canonical\" annotation — in the HTML <head> or an HTTP Link header — that tells search engines which URL is the preferred version of duplicate or near-duplicate content. and react-helmet on CSR) as a fallback, not the plan: Google sees them late, and AI crawlersAI crawlers are bots from AI companies that fetch web pages to train language models, build AI-search indexes, or answer live user questions. They come in three categories, each with its own user-agent tokens and its own robots.txt controls. not at all.
React SEO — cheat sheet
RenderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. modes at a glance
| Mode | Content in initial HTML? | SEO risk | Use for |
|---|---|---|---|
| CSR (raw React) | No | Highest | Logged-in dashboards, not-indexedStoring a crawled page in the search index so it can appear in results. Crawled is not the same as indexed — Google selects what to keep, and indexing isn't guaranteed. apps |
| Pre-renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. (react-snap) | Yes (build time) | Low | Small, mostly-static sites |
| SSG | Yes (build time) | Lowest | Blogs, docs, marketing |
| SSR | Yes (per request) | Low | Fresh, dynamic content |
| ISR / hybrid (Next.js) | Yes | Low | Hourly/daily content |
| Dynamic rendering | BotA crawler — also called a spider or bot — is an automated program that fetches web pages, extracts their links, and queues new URLs to visit. Search engines use crawlers to discover and download content for their index.-only | Deprecated | Don’t — use SSR/SSG/hydrationActivating server-rendered HTML in the browser by attaching JavaScript handlers. |
Common React SEOReact SEO is the practice of making React apps crawlable and indexable. React renders client-side by default, so the raw HTML is near-empty until JavaScript runs — SSR or SSG puts the content back in the initial response where crawlers (and AI bots) can reliably see it. mistakes → fixes
| Mistake | Fix |
|---|---|
| Content only in rendered DOM (CSR) | SSR / SSG / pre-render |
Hash URLs (/#/path) | History API (BrowserRouter) |
onClick navigation, no anchor | Real <a href> / React Router <Link> |
| Meta tagsMeta tags are HTML elements in a page's head that pass metadata about the page to search engines and browsers. For SEO only a few matter — the title element, the meta description, and the robots meta tag — while meta keywords and most others are ignored. don’t update on route | React 19: native <title>/<meta>/<link>. Older/advanced: react-helmet-async. Next.js: Metadata API |
react-helmet (original, any React version) | Switch to react-helmet-async or React 19 native tags |
createRoot used on server-rendered HTML | Use hydrateRoot instead — createRoot discards the server markup |
| Hydration mismatch warning suppressed | Treat it as a bug and fix the server/client difference |
| Slow content → render timeout → dups | Load main content first; move to SSR/SSG |
Client-side 404 returns 200 | Real 404 status or noindex |
Blocked .js / .css in robots.txtA plain-text file at the root of a host that tells crawlers which URLs they may and may not request. It controls crawling, not indexing — a blocked URL can still be indexed if it's linked from elsewhere. | Allow them — Google won’t render blocked files |
| Content gated on cookies/localStorage | Don’t — the renderer is stateless |
Fast rules
- The renderer is evergreen Chromium, queued, stateless, and times out.
- No ranking bonus for SSR — it’s about reliable indexability (Mueller).
- AI-crawlerA crawler — also called a spider or bot — is an automated program that fetches web pages, extracts their links, and queues new URLs to visit. Search engines use crawlers to discover and download content for their index. rendering varies by provider → raw HTML is the safest coverage baseline.
- In Next.js, use the Metadata API — not React Helmet.
- Bing renders JS (via Edge) but less reliably; SSR/SSG is the safe call there too.
Check what the server sends before React runs
Put representative indexable routes in urls.txt. This finds CSR shells and missing
server-rendered head markup in the raw response:
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.txtSmall byte size is only a review signal, not an error by itself. Compare flagged raw responses with rendered HTML and confirm that primary text and crawlable links are present.
Patrick's relevant free tools
- Raw vs. Rendered HTML Checker — See what's in your page's initial HTML versus after JavaScript runs — headless-Chrome rendering only when the page actually needs it, a rendering-strategy verdict (SSR / prerendered / CSR / hybrid), ~15 calibrated JavaScript-SEO checks (noindex, canonicals, robots.txt blocking, links, soft 404s), a side-by-side raw-vs-rendered diff, and shareable reports.
- SEO Incident Simulator — Practice thirty deterministic technical SEO incident investigations — indexability, crawl controls, redirects, sitemaps, markup, caching, DNS, bot verification, rendering, hreflang, and faceted navigation — with clearly labeled fixture evidence and Find → Fix → Verify handoffs.
- Staging vs. Production SEO Diff — Compare matched release URLs across redirects, canonicals, robots directives, hreflang, selected headers, schema eligibility, and raw or optionally rendered content with honest not-evaluated states.
Tools for auditing React SEO
- View Source vs. Inspect Element — the fastest first check. View Source is the raw HTML (what a crawlerA crawler — also called a spider or bot — is an automated program that fetches web pages, extracts their links, and queues new URLs to visit. Search engines use crawlers to discover and download content for their index. gets before JS); Inspect Element is the rendered DOM. Content in Inspect but not View Source is JavaScript-dependent.
- URL InspectionA Google Search Console feature that reports how Google sees one specific URL on a property you own. By default it shows the last-indexed snapshot; a separate \"Test live URL\" mode fetches the current version. (Google Search ConsoleA free Google service that reports how a site performs in Google Search and surfaces problems with how Google crawls, indexes, and serves it. It's first-party data straight from Google — but you don't need it to appear in results.) — the source of truth. Run a live test, then view the rendered HTML, the screenshot, the page resources (what loaded vs. was blocked), and console messages to see exactly what Google rendered.
- Rich ResultsRich results (formerly 'rich snippets') are enhanced search listings — stars, images, prices, breadcrumbs, video thumbnails, and more — that Google and Bing build from structured data. They're a display feature, not a ranking factor, and eligibility never guarantees they'll show. Test — a quick rendered-HTML and structured-data check for a single URL without verifying the site.
- Chrome DevTools — disable JavaScript (Command Menu → “Disable JavaScript”) and reload to inspect what an HTML-only fetcher receives. This is a coverage check, not proof of any named AI crawlerAI crawlers are bots from AI companies that fetch web pages to train language models, build AI-search indexes, or answer live user questions. They come in three categories, each with its own user-agent tokens and its own robots.txt controls.’s current renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. behavior.
- Search ConsoleGoogle's free tool for monitoring crawling, indexing, and search performance. Coverage report — watch “Discovered, currently not indexedStoring a crawled page in the search index so it can appear in results. Crawled is not the same as indexed — Google selects what to keep, and indexing isn't guaranteed.” (render backlog) and duplicate clusters (the render-timeout boilerplate trap).
- JS-renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. crawlersA crawler — also called a spider or bot — is an automated program that fetches web pages, extracts their links, and queues new URLs to visit. Search engines use crawlers to discover and download content for their index. — Ahrefs Site Audit and Screaming Frog SEO Spider (JS-rendering mode) execute JavaScript so you can diff raw vs. rendered HTML across the site.
Mistakes React teams actually make
Concrete patterns I keep seeing in CSR React apps that get shipped, not hypothetical ones. Each is a prevention move — catch it before it costs you indexingStoring a crawled page in the search index so it can appear in results. Crawled is not the same as indexed — Google selects what to keep, and indexing isn't guaranteed..
Shipping raw CRA/Vite CSR for pages that need to rank
Teams ship Create React App or Vite + React straight to production for marketing pages,
blog posts, or product pages — the exact content that needs to show up in search.
Why it’s wrong: the server sends an almost-empty <div id="root"> shell; your real
content only exists after JavaScript runs, so it’s delayed by Google’s render queue and
can be invisible to any AI crawlerAI crawlers are bots from AI companies that fetch web pages to train language models, build AI-search indexes, or answer live user questions. They come in three categories, each with its own user-agent tokens and its own robots.txt controls. that fetches initial HTML without client execution.
What to do instead: move
anything that needs to rank or be cited to SSR or SSG — Next.js or Remix are the easiest
paths — and reserve raw CSR for logged-in, non-indexed surfaces like dashboards.
Routing on hash URLs (HashRouter)
Reaching for React Router’s HashRouter because it’s the path of least resistance —
no server config needed, works on any static host. Why it’s wrong: Google can’t
reliably resolve /#/products-style URLs; the old AJAX-crawlingCrawling is how search engines use automated bots (like Googlebot and Bingbot) to discover URLs and download pages. A page has to be crawlable to be indexed, but crawling on its own isn't a ranking factor. scheme that made hash
fragments crawlable is deprecated. What to do instead: use BrowserRouter (the
History API) and make sure the server responds to every route it produces, including a
direct hit or refresh on a deep link.
Building navigation on onClick instead of real anchors
Wiring up navigation with onClick handlers on a <div> or <button>, often because it
was easier to style or avoid default link behavior. Why it’s wrong: Google only
follows real <a href> links — a <div> with a click handler is invisible to crawlingCrawling is how search engines use automated bots (like Googlebot and Bingbot) to discover URLs and download pages. A page has to be crawlable to be indexed, but crawling on its own isn't a ranking factor.,
no matter how it behaves for a mouse. What to do instead: use React Router’s
<Link> component, which renders an actual <a href> under the hood, or a plain anchor
tag for external navigation.
Loading main content behind a slow API waterfall
Fetching the header and nav fast, then chaining several API calls before the actual page content — the part that makes each URL unique — appears. Why it’s wrong: Google’s Web Rendering ServiceTurning HTML, CSS, and JavaScript into the final visual page and DOM. enforces a timeout; if your centerpiece content loads slowly, renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. finishes before it arrives, and Google is left indexingStoring a crawled page in the search index so it can appear in results. Crawled is not the same as indexed — Google selects what to keep, and indexing isn't guaranteed. boilerplate-only pages that then get flagged as duplicates of each other — the exact failure mode Gary Illyes described. What to do instead: restructure requests so main content loads first, or remove the dependency on client-side rendering entirely with SSR/SSG.
Still using the original react-helmet
Reaching for react-helmet for per-route <title> and meta tagsMeta tags are HTML elements in a page's head that pass metadata about the page to search engines and browsers. For SEO only a few matter — the title element, the meta description, and the robots meta tag — while meta keywords and most others are ignored. because it’s the
library every older tutorial recommends. Why it’s wrong: the original package is
unmaintained — no release since 2020 — and has known issues under React 18’s concurrent
rendering and SSR. What to do instead: on React 19, render <title>/<meta>/<link>
directly in your components and let React hoist them (no library needed for the basics).
On React 18, or for advanced needs like SSR context serialization or titleTemplate, use
react-helmet-async, the maintained fork. On Next.js, use 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, sometimes left over
from an old crawl-budget concern or copied from another site’s config. Why it’s
wrong: Google can’t render what it isn’t allowed to fetch — a blocked bundle means the
Web Rendering Service builds an incomplete (or empty) DOM, even though your source code
is fine. What to do instead: allow crawlersA crawler — also called a spider or bot — is an automated program that fetches web pages, extracts their links, and queues new URLs to visit. Search engines use crawlers to discover and download content for their index. to fetch your JS and CSS, and confirm it
with the URL Inspection toolA Google Search Console feature that reports how Google sees one specific URL on a property you own. By default it shows the last-indexed snapshot; a separate \"Test live URL\" mode fetches the current version.’s page-resources check to see nothing critical is blocked.
Test yourself: React SEO
Five quick questions on making React apps crawlable and indexable. Pick an answer for each, then check.
Resources worth your time
My related writing
- React SEO: Best Practices to Make It SEO-Friendly (Ahrefs) — the React SEOReact SEO is the practice of making React apps crawlable and indexable. React renders client-side by default, so the raw HTML is near-empty until JavaScript runs — SSR or SSG puts the content back in the initial response where crawlers (and AI bots) can reliably see it. guide on Ahrefs, which I reviewed; this article is the deeper, source-linked treatment.
- JavaScript SEO: A Definitive Guide (Ahrefs) — my full guide to the underlying renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. mechanics: DOM parityWhether the rendered DOM matches what you expect the raw HTML to become., the most-restrictive-directive rule, canonical and meta-tag handling, and renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. choices. Read this for the general case behind React-specific fixes.
- The Beginner’s Guide to Technical SEO (Ahrefs) — where React/JavaScript SEOMaking sure search engines can crawl, render, and index content that depends on JavaScript. fits in the bigger picture.
My speaking
- How Search Works (SlideShare) — my walkthrough of crawlingCrawling is how search engines use automated bots (like Googlebot and Bingbot) to discover URLs and download pages. A page has to be crawlable to be indexed, but crawling on its own isn't a ranking factor., rendering, indexingStoring a crawled page in the search index so it can appear in results. Crawled is not the same as indexed — Google selects what to keep, and indexing isn't guaranteed., and ranking. (My standing disclaimer applies: “This is my understanding of systems… not going to be 100% complete or accurate.”)
From around the industry
- Understand JavaScript SEO Basics (Google) — primary-source docs on SPAs, the History API, and canonical/status-code handling.
- Dynamic Rendering (deprecated) (Google) — why dynamic rendering is a workaround, not a long-term solution.
- Martin Splitt Explains How JavaScript Sites Are Indexed (Search Engine Journal) — the “barebone HTML … boom” explanation of the crawl → render → index flow.
- Gary Illyes on JS-heavy sites and duplicate content (LinkedIn) — the render-timeout-to-duplicates failure mode, in his own words.
- How to fix technical SEO issues on client-side React apps (Search Engine Land) — a real-world case study on auditing and fixing a CSR React app.
- SSR vs. dynamic rendering — no ranking difference (Search Engine Roundtable) — Mueller’s “no SEO ranking bonuses” statement.
- react-helmet-async (npm) — the maintained head-management library for standalone React.
- The new evergreen Bingbot (Microsoft Edge) (Bing) — BingbotBingbot is Microsoft Bing's primary web crawler — the bot that discovers, fetches, and renders pages to build the Bing index. That index also powers Yahoo, DuckDuckGo, Ecosia, and Microsoft Copilot, so Bingbot's reach is far wider than Bing's own search-market share. rendering JS via Chromium, like GooglebotGooglebot is Google's web crawler — the software that fetches pages so Google can index and rank them. It comes in two variants, Googlebot Smartphone (primary, under mobile-first indexing) and Googlebot Desktop, and runs an evergreen Chromium renderer..
Videos
- Google Search Central — JavaScript SEOMaking sure search engines can crawl, render, and index content that depends on JavaScript. series (YouTube) — Martin Splitt’s official video series covers SEO for React, Angular, and Vue specifically, walking through the crawl → render → indexStoring a crawled page in the search index so it can appear in results. Crawled is not the same as indexed — Google selects what to keep, and indexing isn't guaranteed. process and the common fixes. Series announcement · Channel
React SEO
React SEO is the practice of making React apps crawlable and indexable. React renders client-side by default, so the raw HTML is near-empty until JavaScript runs — SSR or SSG puts the content back in the initial response where crawlers (and AI bots) can reliably see it.
Related: JavaScript SEO, Headless CMS SEO
React SEO
React SEO is the discipline of making web apps built with React — Meta’s component-based JavaScript library — discoverable and rankable in search engines.
The core problem is renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM.. By default (Create React App, Vite + React), React uses client-side renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. (CSR): the server returns a near-empty HTML shell — typically just a <div id="root"></div> — and the browser runs JavaScript to build the actual page. A crawlerA crawler — also called a spider or bot — is an automated program that fetches web pages, extracts their links, and queues new URLs to visit. Search engines use crawlers to discover and download content for their index. that fetches the initial HTML sees little or no content until it executes that JavaScript. Google can render React via its Web Rendering Service (an evergreen Chromium), but rendering is queued separately from crawlingCrawling is how search engines use automated bots (like Googlebot and Bingbot) to discover URLs and download pages. A page has to be crawlable to be indexed, but crawling on its own isn't a ranking factor., can be delayed, and can time out — and most AI crawlersAI crawlers are bots from AI companies that fetch web pages to train language models, build AI-search indexes, or answer live user questions. They come in three categories, each with its own user-agent tokens and its own robots.txt controls. don’t run JavaScript at all.
React SEO is about closing that gap: choosing a rendering strategy that puts content in the initial HTML (server-side rendering or static site generation, most easily via Next.js or Remix), managing <head> metadata per route (react-helmet-async, not the deprecated react-helmet), using History API routing instead of hash URLs, keeping links as real <a href> anchors, and returning correct HTTP status codesAn HTTP status code is the three-digit number a server returns with every response to tell a browser or crawler what happened to its request — success, redirect, client error, or server error. For SEO the code matters as much as the content: it tells Google and Bing whether to index a page, follow a redirect, retry later, or drop the URL from the index. for client-side routes. React isn’t bad for SEO — but client-side rendering by default creates issues that have to be understood and addressed.
Related: JavaScript SEO, Headless CMS SEO
Build-time retrieval analysis plus live signals for this exact article. The automatic chunk report includes a deterministic readiness score and is ready without a model download.
Search Console
sampleGA4 traffic (28d)
sampleCloudflare traffic (7d)
sampledCrUX field data (28d, phone)
sampleGoogle NLP entities
localChangelog
Updated Jul 18, 2026.
Editorial summary and recorded change details.Summary
Added the React 19 native metadata-hoisting story (verified live against react.dev and react-helmet-async's own docs) and a version-scoped createRoot vs. hydrateRoot section covering hydration-mismatch handling, closing the gaps the improvement packet flagged around React-the-library vs. its default CSR toolchain.
Change details
-
Added a React 19 native <title>/<meta>/<link> hoisting explainer to the metadata section, scoped by React version (19 native, 18 or advanced needs react-helmet-async, Next.js Metadata API regardless) — verified react-helmet-async is actively maintained (v3.x, React-19-aware) and the original react-helmet is unmaintained since 2020, both via live source checks.
-
Added a new 'Hydration must match exactly' section distinguishing createRoot (CSR) from hydrateRoot (attaches to server-rendered HTML), grounded in react.dev's official caveat that React does not guarantee patching attribute mismatches — updated the checklist, cheat sheet, quiz, and AI summary to match.
-
Clarified that React the library supports CSR/SSR/static/streaming rendering; the empty-shell problem belongs to the default CRA/Vite toolchain, not to React itself.
Full comparison unavailable — no prior snapshot was archived for this revision.