React SEO: make your React site visible to Google
React renders in the browser. Googlebot doesn't wait, so it reads an empty HTML shell and moves on. Your content exists - most crawlers never see it.
This is a known problem, and the fix is simpler than you think.
Don't want an external tool? We understand.
If you've got the engineering weeks to migrate to Next.js App Router, restructure your data loading, handle server/client state transfer, and chase down hydration bugs, that's a permanent fix. PageGlass is a five-minute alternative when that rewrite isn't on your roadmap.
Fix this in three steps
- Run a free React scan. Confirm the bot visibility gap caused by client-side React rendering - the scan takes ten seconds and needs no signup. You get exactly what Googlebot, Bingbot, and the AI crawlers see when they hit your URLs today, side by side with what your users see.
- Add one CNAME record. Point your React site's domain through PageGlass in your DNS provider. The change takes thirty seconds and is fully reversible. Zero code changes, no deploy, no impact on the bundle your real users download.
- React content is now indexable. Bot traffic now receives fully-rendered HTML for every URL on your React site, while human visitors keep getting the exact same dynamic experience as before. Come back to PageGlass and run the scan again to confirm the fix.
Why React specifically struggles with crawlers
React's entire model is based on rendering components in the browser. When
you run create-react-app or vite create, you get a
project template where the production index.html contains a
single empty <div id="root"></div> and a
<script> tag pointing at a JavaScript bundle. Everything
else (your routes, your components, your meta tags) is produced in the
browser after React mounts.
Googlebot can execute JavaScript, but it does so in a second pass that can be delayed by days or weeks. Other crawlers (Bingbot, GPTBot, ClaudeBot, PerplexityBot, Facebook's share bot, Twitter's card fetcher, LinkedIn's preview bot) either don't execute JavaScript at all or skip it for performance. So the moment your content depends on React hydration, most crawlers see an empty page.
Common fixes that don't actually work
- react-helmet-async or react-helmet. These update the document head after React mounts. Crawlers that read the raw HTML response never see the updates. Useful for human browsers, invisible to most bots.
- useEffect to set document.title. Same problem. The effect runs after mount. The crawler has already moved on.
- Setting meta tags in the router loader. Still client-side. Still invisible to non-JavaScript crawlers.
- Google Search Console URL inspection "looks fine". The URL inspection tool uses Google's rendering service, which executes JavaScript. That doesn't mean Google's crawler does on the first pass, and it definitely doesn't mean other crawlers do.
What actually works
Three paths: (1) move to server-side rendering via Next.js App Router or Remix, which produces HTML on the server before sending it to the browser; (2) static-site-generate your entire app at build time, which works for content-heavy sites with stable URLs; or (3) use a prerendering service like PageGlass, which renders each page once on request and serves the rendered HTML to bots without touching your React code.