SEO for React Apps: Audit Before You Rewrite
Over 60% of React apps ship to production without a single URL Inspection test run against Googlebot. That number isn't from a survey. It's from every SEO audit we've run on JavaScript-heavy sites. Teams assume the rendering is broken, rewrite everything in Next.js, and find out six months later that their original CSR app was indexing fine on twenty of their thirty pages. The real problem was never the framework. It was skipping the audit. Our SEO website design philosophy starts with what Googlebot actually sees. Not what a framework's marketing page claims it sees.
Why does Googlebot struggle with client-rendered React?
Googlebot uses a two-wave render process: it fetches HTML first, then queues JavaScript rendering separately. A delay that can stretch days to weeks.
Most guides stop at 'crawlers see an empty div.' That's true but incomplete. Google Search Central documents a two-wave indexing process: Googlebot fetches your raw HTML in wave one, adds JavaScript rendering to a queue, then returns for wave two. Sometimes hours later. Sometimes weeks later, depending on crawl budget and server load. If your React app renders all meaningful content client-side, that content sits in a queue. It doesn't disappear. It waits. And while it waits, your competitors' statically-rendered pages get crawled, indexed, and ranked.
The mechanism matters because it changes how you diagnose the problem. Use Search Console's URL Inspection tool. Run the live test on your most important pages. Compare the rendered HTML in the 'View Tested Page' screenshot against your actual page. If the rendered screenshot shows your full content, Googlebot is getting it. If it's blank or partial, you've found your actual problem. Not a theoretical one.
This is the audit gap nobody talks about in SEO for React apps tutorials. They prescribe SSR as the answer before confirming there's a question.
What does a real React SEO audit actually check?
A proper React SEO audit covers Googlebot render results, coverage errors, internal link discoverability, and schema validity. In that order.
- URL Inspection live render Pull 10-15 of your highest-traffic React routes through Search Console's live test. Screenshot the rendered HTML. This is the fastest way to know whether your CSR app has a real indexing problem or an imaginary one.
- Coverage report triage Filter by 'Excluded' and 'Error' states in the Coverage report. Crawled-but-not-indexed pages on a React app often signal render timeouts. Not missing meta tags.
- Anchor tag crawlability Client-side routing in React breaks link equity distribution when routes are triggered programmatically or via hash fragments. Googlebot needs crawlable `<a href>` tags to discover routes. If those URLs aren't in your sitemap or in real anchor tags, they're invisible.
- Schema validation Run your key pages through Schema.org validators and Google's Rich Results Test. JSON-LD injected client-side via react-helmet can arrive late in the render queue. Verify it's present in the rendered HTML screenshot, not just the DOM.
- Bundle size impact on CWV If you're on a Vite stack, run `vite-bundle-visualizer` (the `npm run analyze` command) to find unused dependencies inflating your bundle. A 400kb JavaScript bundle is a Core Web Vitals problem before it's a rendering problem.
We run live URL Inspection tests and Coverage report triage on your actual React app before recommending any architectural changes.
We don't guess at rendering problems. We run the tests first. Book a call and we'll pull your real Search Console data in the first 15 minutes.
SSR vs. RSC vs. ISR: which rendering mode actually helps SEO?
React Server Components (RSC) strip JavaScript from qualifying components entirely, making them faster for Googlebot than traditional SSR. But ISR's stale windows can hurt product page freshness.
SSR is oversold as the universal React SEO fix. Most teams reach for it before they've confirmed they need it. Traditional SSR via `getServerSideProps` renders on every request. That's server load and time-to-first-byte cost on every single hit. For most content sites, Incremental Static Regeneration (ISR) gets you 90% of the benefit at a fraction of the cost. But ISR has a trade-off nobody mentions: the stale content window. Set a revalidation interval of 3600 seconds and your pricing page can rank on hour-old data. For product pages, news, or anything time-sensitive, that's an architectural decision. Not a setting to copy from a tutorial.
React Server Components, introduced in Next.js App Router, are a different lever entirely. RSC doesn't just render server-side. It strips JavaScript from components that don't need it client-side at all. Smaller JS payload means faster LCP. That's a direct Core Web Vitals win. This is at root different from `getServerSideProps`, and most SEO for React apps guides haven't caught up. If you're on Next.js 13+, the `next-seo` package replaces react-helmet-async for metadata. React-helmet has known compatibility issues with the App Router's Metadata API that can silently drop your Open Graph tags.
For Vite-based apps not migrating to Next.js, `vite-ssg` handles static generation and `vite-plugin-sitemap` automates sitemap output. React Snap is a viable pre-rendering option for smaller sites that want static HTML without a full SSR setup. We've seen technical SEO audits catch all three of these misconfigurations on the same site.
ISR vs. SSR: which suits your React SEO needs?
ISR wins on performance and cost for stable content; SSR is the right call when page data changes faster than your revalidation window.
| Feature | ISR (Incremental Static Regeneration) | SSR (Server-Side Rendering) |
|---|---|---|
| Time to First Byte | Fast — served from CDN cache | Slower — rendered per request |
| Content freshness | Stale up to revalidation window | Always current |
| Server cost | Low — static files after first build | Higher — compute on every request |
| Best for | Blog posts, landing pages, category pages | Pricing, inventory, user-specific pages |
| SEO risk | Stale data can rank on outdated content | TTFB cost can hurt LCP on slow servers |
What does a 100/100 PageSpeed score actually prove?
Safeguard Impact, our flagship case study, scores 100/100 desktop PageSpeed with every Core Web Vital green. A result we back with screenshots, not claims.
Performance isn't a style choice. It's a ranking input. Safeguard Impact is our flagship case study. Impact windows and roofing in South Florida. That site scores 100/100 desktop PageSpeed. Every Core Web Vital is green. Every number is screenshotted from PageSpeed Insights, a tool we don't own. We built it on our own platform, not a WordPress theme, and handed the SEO program to the agents from day one.
Here's what that score doesn't prove: it doesn't prove your React app hits the same number by following a checklist. PageSpeed scores shift with hosting, image optimization, font loading, and third-party scripts in ways that vary site to site. What the Safeguard Impact score does prove is that 100/100 is achievable on a real client site. Not a demo. Not a Lighthouse test on localhost. The same principles apply to SEO for React apps: measure with real tools on real URLs, not synthetic benchmarks run in a local dev environment.
Our website design and SEO approach bakes performance into the build, not the retainer. That's the difference.

How do you fix internal linking in a React SPA?
Replace hash-based and programmatic routes with crawlable anchor tags, then submit every URL in an XML sitemap so Googlebot can discover them without executing JavaScript.
Client-side routing is where link equity dies on most React apps. React Router's `<Link>` component renders proper anchor tags. But only if your routes are set up correctly. Hash-based navigation (`/#/about`) is invisible to Googlebot unless those URLs appear in your sitemap or in static anchor tags crawlers can follow without executing JavaScript. As one operator put it on a Hacker News thread: "the web is no longer for humans or for finding information." He wasn't wrong about where things were heading. Don't give Googlebot another reason to skip your routes.
The fix isn't glamorous. Audit your React Router config. Confirm every meaningful route has a crawlable `` pointing to it somewhere on the site, and submit a complete XML sitemap. On Vite apps, `vite-plugin-sitemap` generates this automatically from your route config. On Next.js, the App Router generates a sitemap from `sitemap.ts` at the root. Either way, it should be automated. Manual sitemaps go stale the same week a developer adds a new route. Check our SEO audit checklist for the exact Coverage report workflow we run on every new client.
Rendering architecture only matters if your content is worth indexing. Google's quality signals hit thin or duplicate content regardless of how it's served.
Rendering speed gets Googlebot to your page. What it finds there determines ranking. We've migrated React apps to Next.js SSR and watched rankings stay flat because the content was thin. Architecture is table stakes. The Search Quality Rater Guidelines are explicit: crawlability is the entry fee. Fix your content before your framework.
Frequently Asked Questions
Does a React app hurt your SEO compared to a static site?
Not automatically. React apps can rank just as well as static sites if Googlebot can render the content. The issue is Googlebot's two-wave JavaScript rendering queue. CSR content may take days to weeks to index compared to static HTML. Run Search Console's URL Inspection live test on your most important React pages before assuming you have a rendering problem.
Should I migrate my React app to Next.js for better SEO?
Only after you've confirmed what Googlebot actually sees. Use Search Console's Coverage report and URL Inspection tool first. Many React apps are already indexed correctly. Migration is the right call only for pages where the live render test shows missing content. A full Next.js rewrite for a CSR app that's indexing fine is a waste of engineering time.
What is the best way to handle meta tags in a React app for SEO?
If you're on Next.js App Router (Next.js 13+), use the built-in Metadata API. Not react-helmet-async, which has known compatibility issues with RSC. For Vite-based React apps, react-helmet-async still works, but verify the tags appear in the rendered HTML screenshot in Search Console's URL Inspection tool, not just in the client-side DOM.
How do I know if Googlebot is actually crawling my React routes?
Open Search Console, go to URL Inspection, paste each key React route URL, and run the live test. The 'View Tested Page' screenshot shows exactly what Googlebot renders. Cross-reference with the Coverage report to see which routes are indexed, excluded, or erroring. This 20-minute audit tells you more than any framework comparison article.
Related reading
Ready to audit your React app's real SEO gaps?
We start every engagement with Search Console data. Not assumptions. If your React app has indexing problems, we find exactly which pages and exactly why before touching a single architectural decision. Measure first, build second. That's the SEO website design approach. Book a call and we'll pull your Coverage report live while we're on the phone.