React SEO: Making Client-Rendered Apps Crawlable
Client-rendered React ships empty HTML and search engines may never see your content. Here's why it happens and how to fix it with SSR, SSG, and the right framework.

TL;DR — A default client-rendered React app serves nearly empty HTML and builds the page in the browser, which means search engines and AI crawlers can see a blank shell. The fix is to render meaningful HTML on the server — via SSR, SSG, or a framework that does it for you — plus real metadata and structured data.
- The problem: client-side rendering (CSR) hands crawlers an empty
<div id="root">and a JavaScript bundle. - The fix: server-render or pre-render your content with Next.js, Remix, or Astro, and set per-page metadata.
- The catch: Googlebot renders JavaScript (slowly); most AI crawlers don't run it at all.
If you build with React and care about organic and AI search, this is one of the highest-leverage technical issues to get right. It's a core piece of SEO for developers — the kind of problem that's invisible in the browser but decides whether you ever get indexed.
The core React SEO problem
Open the page source of a plain Create React App or Vite single-page app and you'll find something like this:
<body>
<div id="root"></div>
<script src="/static/js/bundle.js"></script>
</body>
That's it. No headline, no copy, no links — just an empty container and a script tag. Your content only exists after the browser downloads the bundle, executes it, fetches data, and React paints the DOM. This is client-side rendering: the server sends a near-empty document and the client does all the work.
A human with a fast device barely notices. A crawler is a different story.
Search engines. Googlebot can render JavaScript, but it does so in a deferred, two-wave process: it crawls the raw HTML first, then queues the page for rendering when resources free up. That render can lag hours or days behind, and anything that breaks — a slow API, a JS error, a blocked script — can leave your content unseen. Bing and most other engines render JavaScript far less reliably, if at all.
AI crawlers. This is the sharper edge in 2026. The bots behind AI answers — GPTBot, ClaudeBot, PerplexityBot, Google-Extended — overwhelmingly fetch raw HTML and don't execute JavaScript. If your content isn't in the initial HTML response, it effectively doesn't exist to them. As more discovery shifts to AI assistants, a CSR-only app risks being invisible exactly where attention is moving.
So the failure mode is simple: your content is real, but the machines that decide visibility never see it.
Why it matters
Three concrete consequences follow from shipping empty initial HTML:
- Delayed or skipped indexing. Pages that depend on a second render wave get indexed later, or get dropped when rendering fails.
- Weak or missing snippets. Without server-rendered
<title>and meta description, your search result can show a generic or empty snippet. - Zero AI citations. If ClaudeBot or GPTBot can't read your page, you can't be summarized or cited in an AI answer.
For a marketing site, docs, or blog, this is the whole game. Crawlability isn't a nice-to-have — it's the precondition for everything else you do in SEO.
The fixes, step by step
The goal is the same in every case: make sure the crawler receives meaningful HTML in the first response. Here are the approaches, roughly in order of how most teams should reach for them.
1. Choose a rendering strategy
These are the three core strategies and how they affect crawlability:
| Strategy | When HTML is built | Crawler sees content | Best for |
|---|---|---|---|
| CSR (client-side) | In the browser, after JS runs | Only if the bot executes JS | App-like dashboards behind auth |
| SSR (server-side) | On each request, on the server | Yes, immediately | Dynamic, frequently changing pages |
| SSG (static generation) | Once, at build time | Yes, immediately | Blogs, docs, marketing pages |
CSR is fine for the logged-in parts of a product no one needs to find in search. For anything you want crawled, you want SSR or SSG so the HTML arrives populated.
2. Reach for a framework
You can wire up SSR by hand with renderToString, but in practice almost no one should. A React meta-framework gives you server rendering, routing, and metadata handling out of the box:
- Next.js — the default choice for most React teams. The App Router server-renders by default; you opt into client behavior with
'use client'. Supports SSR, SSG, and incremental static regeneration in one app. - Remix / React Router — server-rendering-first, with a strong data-loading model that keeps content in the initial HTML.
- Astro — ships zero JavaScript by default and renders components to static HTML, with React "islands" only where you need interactivity. Excellent for content-heavy sites.
If you're starting a new content or marketing site in React, pick one of these rather than a bare SPA. It removes the entire class of problem this article is about. If you're on Vercel, Next.js is the path of least resistance.
3. Pre-render an existing SPA (the stopgap)
Already shipped a client-rendered app and can't migrate yet? You have intermediate options:
- Static prerendering at build time — tools that crawl your routes during the build and save a fully rendered HTML snapshot per page. Good for sites with a finite, mostly static set of URLs.
- Dynamic rendering — detect crawler user-agents and serve them a server-rendered or pre-rendered version while users get the SPA. Google treats this as a legitimate workaround, but it's a maintenance burden and a stopgap, not a destination. Prefer real SSR/SSG when you can.
4. Set real metadata per page
Server-rendered HTML is necessary but not sufficient — each page still needs its own <title>, meta description, canonical, and Open Graph tags in that initial response.
- In a framework: use the built-in head/metadata API (Next.js
metadataexport orgenerateMetadata, Remixmeta, Astro frontmatter). These render the tags on the server, where crawlers will see them. - In a plain SPA: a library like
react-helmet-asyncmanages document head tags — but remember it only helps crawlers if those tags exist in server-rendered or pre-rendered HTML, not just after hydration.
Per-page metadata is what turns a crawlable page into a good search result.
5. Add structured data
Structured data (JSON-LD) helps both search engines and AI crawlers understand what a page is — an article, a product, an FAQ. Inject a <script type="application/ld+json"> block, server-side, matching the page type (Article, Product, FAQPage, BreadcrumbList). Because it's plain text in the HTML, even JS-free AI crawlers can parse it, which makes it disproportionately valuable for AI search.
Common mistakes
- Assuming "Googlebot renders JS" means you're fine. It renders eventually and unreliably, and most other crawlers — including the AI ones — don't render at all. Don't lean on it.
- Hydration that overwrites server HTML. A mismatch between server and client markup can blank out content or throw errors; keep the server and client trees identical.
- Metadata set only on the client. If
react-helmetruns after hydration, crawlers reading raw HTML never see your title or description. The tags must be in the server response. - Blocking your own JS/CSS in
robots.txt. If Googlebot can't fetch the bundle, its render wave fails. Don't disallow/static/or/_next/. - Treating dynamic rendering as permanent. It's a bridge while you migrate to SSR/SSG, not the finish line.
Frequently asked questions
Is React bad for SEO?
No — React itself is neutral. The problem is client-side rendering, which is React's default in a bare SPA. Render your content on the server with SSR or SSG and React is perfectly SEO-friendly.
Does Google index client-side React apps?
Sometimes, and slowly. Googlebot can execute JavaScript in a deferred second wave, so CSR pages can get indexed — but with delays and a real risk of failure. Server-rendered HTML is indexed immediately and reliably.
Do I have to use Next.js for React SEO?
No, but a meta-framework makes it dramatically easier. Next.js, Remix, and Astro all produce crawlable HTML out of the box. You can hand-roll SSR or prerender a SPA, but a framework removes most of the work and the footguns.
Will AI search engines like ChatGPT see my React site?
Only if your content is in the raw HTML. Crawlers like GPTBot and ClaudeBot generally don't run JavaScript, so a CSR-only app is invisible to them. SSR/SSG plus structured data is what gets you readable and citable.
How do I check if my React app is crawlable?
View the page source (not the rendered DOM) or fetch the URL with curl — if your content and metadata aren't in that response, crawlers may not see them. You can also check how your site reads to AI assistants with an AI-readiness checker, and lean on tools like SEOAgent to catch missing metadata and structured data as you build.
Conclusion
React's SEO problem isn't React — it's shipping an empty HTML shell and trusting machines to fill it in. Search engines do that unreliably; AI crawlers usually don't do it at all. Render your content on the server with SSR or SSG, set real per-page metadata, and add structured data, and you turn an invisible SPA into a page that gets indexed and cited.
If you want a deeper map of how crawlability, performance, and metadata fit together, start with SEO for developers — and if your site lives in a codebase, an SEO engine that works alongside your coding agent can keep these fixes in place as the app evolves. For more on the broader landscape, see our roundup of the best AI SEO tools.
Put SEO on autopilot in your own editor
SEOAgent runs as a free skill inside Claude Code, Cursor, and Codex — on the model you already pay for. Audit, plan, and write SEO content right in your repo, with every change reviewed before it ships. No second AI subscription.
Get SEOAgent free