React SEO is the practice of making a React application legible to search engines that were built for HTML documents. React ships an almost empty HTML file and a JavaScript bundle that assembles the page in the browser. Googlebot can execute that bundle. It does not always do it quickly, it does not do it for every URL, and several crawlers that now matter for traffic do not do it at all.
That gap is where rankings go missing. A React build can pass every Lighthouse check, look perfect on screen, and still return a body element containing one empty div to anything reading the raw response.
This guide covers the whole surface: how Googlebot handles JavaScript, the four rendering strategies and when each one is right, where your data gets loaded, hydration and the failures it hides, dynamic rendering, meta tags, routing, Core Web Vitals, structured data, gated content, and the order to fix things in. ProStar SEO runs this work on client React builds, so the recommendations here are the ones we apply, not a survey of what is written elsewhere.
Is React Good for SEO? What Actually Breaks
React is neutral. The framework does not carry a ranking penalty, and Google has never described one. What React changes is when your content exists. In a server-rendered stack, content exists at the moment of the response. In a default React build, content exists after the browser downloads, parses and executes JavaScript. Search engines pay for that delay in crawl budget, and you pay for it in indexation.
So the honest answer is conditional. React is fine for SEO when the HTML that leaves your server already contains the content. React is a liability when it does not.
How Googlebot Crawls, Renders and Indexes JavaScript
Googlebot handles a URL in three stages. It crawls the URL and reads the raw response. It queues the URL for rendering in a headless Chromium instance. It indexes whatever the rendered DOM contains. Those stages are separate systems with separate budgets, and the second one is the bottleneck.
Google’s own documentation is unambiguous that JavaScript is executed. Martin Splitt has spent years saying so. What the documentation does not promise is when, and the interval between crawl and render is where a React app loses ground to a competitor whose HTML is complete on arrival.
Bingbot is less generous than Googlebot with JavaScript execution, and the crawlers behind AI answer engines are less generous still. Most of them read the raw response and stop. Google Search Central documents the three stages; nothing in that documentation promises a rendering deadline.
How Search Engines Queue JavaScript: the Second Wave and Crawl Budget
The rendering queue is a real queue with real latency. Google’s engineers have described indexing JavaScript pages as a second wave: the first pass indexes what the raw HTML holds, the second pass indexes what rendering produced. On a small site the second wave arrives fast enough that nobody notices. On a catalogue with 80,000 URLs it does not.
Crawl budget makes this worse rather than better. Every render costs Google compute. A site that forces rendering on every URL gets fewer URLs processed per unit of crawl than a site that serves complete HTML, which is why large React commerce sites often show a long tail of URLs discovered but never indexed.
Server logs settle it faster than any tool. Filter your access log to verified Googlebot, group hits by template, and compare that against the URLs in your sitemap. A React catalogue in trouble shows the same shape every time. Googlebot hits a handful of hub pages hard, crawls the deep pages that carry the money thinly, and leaves a long list of URLs fetched once months ago and never again.
That is not a content problem. It is a rendering cost problem showing up as a crawl budget pattern.
The pages you lose are rarely your homepage. They are the deep, high-intent, low-authority URLs that were already marginal.
Three stages, three very different costs: the crawl reads the raw HTTP response in milliseconds, the render waits in a queue running headless Chromium with no published service level, and the index records whatever the rendered DOM held at that moment.
View Source vs Inspect Element: Where SEO and React Disagree
Inspect Element shows you the DOM after JavaScript ran. View Source shows you what the server sent. Developers work in the first view all day, which is exactly why the problem stays invisible inside engineering teams. SEO and React disagree on which of those two documents counts, and search engines side with View Source for the first pass.
Run the test on your own site right now. Open View Source, search for a sentence from your main content, and see whether it is there. If the only thing in the body is <div id="root"></div>, your initial HTML holds nothing and every ranking you hold is riding on the rendering queue.
Google Search Console’s URL Inspection tool settles the argument. It shows the rendered HTML Googlebot actually got, alongside any resources it failed to load. When ProStar SEO audits a React build, that screenshot is the first artefact we pull, because technical SEO decides whether content ranks at all, and it converts an argument about frameworks into a fact about one URL.
Book a Free React SEO Audit with ProStar SEO
We will send you the rendered HTML Googlebot sees on your ten most valuable URLs. No contract, no obligation, and you keep the findings whether or not you work with us.
SEO with React Starts with One Decision: Where the HTML Gets Built
Every other fix in this guide is downstream of one choice: at what moment does your HTML acquire its content? There are four answers, and React supports all of them. Build time. Request time. In the browser. Some mix of the three.
Pick that first. Meta tags, structured data and Core Web Vitals are all easier to fix afterwards, and none of them rescue a page whose content never reaches the crawler.
The decision is per template, not per site. Run through your templates and ask two questions of each: does this page need to rank, and how often does its content change? A marketing page that changes monthly and must rank is a static build. A product page in a 50,000-item catalogue that must rank and changes daily is ISR. A checkout that must never rank can stay client-rendered forever.
Most React estates end up running three of the four strategies side by side. That is the correct outcome, not a sign of inconsistency, and server-side rendering does not have to win everywhere to fix your indexation.
| Strategy | HTML built | Best for | React SEO risk |
|---|---|---|---|
| CSR | In the browser | Dashboards, internal tools | High |
| SSR | Per request, on the server | Personalised, frequently changing pages | Low |
| SSG | At build time | Docs, blogs, marketing pages | Very low |
| ISR | Build time, then revalidated | Large catalogues | Low |
Client-Side Rendering (CSR): the React Default That Costs You Indexing
Client-side rendering is what create-react-app and a plain Vite React template give you. The server returns a shell, the browser downloads a bundle, React mounts into an empty root node and paints the page. CSR produces a clean developer experience and an empty document.
The cost lands on indexing. A CSR page depends on the rendering queue for every word it contains, and any JavaScript error during mount produces a page with no content at all, which Google frequently classifies as a soft 404.
Shopify Hydrogen storefronts, Vite SPAs and legacy create-react-app builds all sit in this category unless someone changed the rendering path deliberately. Our Shopify SEO agency work runs into Hydrogen builds shipping thin initial HTML often enough that we now check it before anything else.
CSR has exactly one right use. Screens behind a login that you never wanted indexed.
Server-Side Rendering (SSR) with Next.js, Remix and Express
Server-side rendering executes React on the server and returns finished HTML. The crawler reads content in the first pass, because the initial HTML already contains it. The rendering queue stops being a dependency and becomes an optimisation.
Next.js is the default answer, and has been since the App Router made server rendering the baseline rather than an opt-in. Remix takes a different route to the same place, leaning on web standards and loaders that run on the server before the response is sent. A custom Express server calling renderToString still works and still ships in plenty of production stacks, though you inherit the caching, streaming and error handling that Next.js and Remix already solved.
SSR costs you time to first byte. The server now does work it previously delegated to the browser. That trade is almost always worth it, and a CDN in front of the origin removes most of the penalty.
Static Site Generation (SSG) and Incremental Static Regeneration (ISR)
Static site generation renders every page at build time and serves plain files. Nothing beats it: the crawler gets complete HTML from a CDN edge in a few milliseconds, and there is no server to fall over. Gatsby built its identity on this, and Next.js does it with generateStaticParams.
The limit is scale. A 200,000-URL catalogue cannot rebuild in CI every time a price changes. Incremental static regeneration solves that: pages are generated statically, served from cache, and regenerated in the background after a revalidation window you set per route. The crawler always gets a static response, and the content is never more stale than your window allows.
ISR is also the rendering mechanism behind most programmatic SEO at scale. Template-generated pages need to be cheap to serve and current enough to be useful, and ISR is the only strategy in the table that delivers both.
React and SEO: Server Loading vs Client Loading
Choosing SSR does not finish the job. A page can be server-rendered and still ship empty, because the rendering strategy governs the shell while the data-fetching pattern governs the content. Most guides skip it. That distinction is where we find the largest share of live defects on React builds.
The question to ask of every component: does the data this renders exist before the response is sent, or after?
Data Fetched on the Server Ships Inside the Initial HTML
Server loaders run before the response. A Next.js server component that awaits its own query, or a Remix loader that returns data to the route, produces markup with the content already inside it. The crawler receives product names, prices, article bodies and reviews as text in the initial HTML.
This is the state you want for anything you expect to rank, and it is decided at the server, before a single byte reaches the browser. Verify it the same way every time: request the URL with curl, search the response for the content, and confirm it is there before a browser has run a single line of JavaScript.
curl -s https://example.com/products/widget | grep -i "widget specification"
Data Fetched in useEffect Arrives After Googlebot Has Left
useEffect runs after mount, in the browser, never on the server. Any content fetched there is invisible in the initial HTML by definition, and this pattern survives migrations: teams move to Next.js, keep their old data-fetching hooks, and ship a server-rendered shell wrapped around client-loaded content. The page looks migrated. The HTML has not changed.
The consequences are not evenly distributed. Googlebot will usually catch up. LLM crawlers largely will not, and citations in ChatGPT, Perplexity and Google’s AI surfaces are increasingly decided on the raw response. That is a growing share of qualified traffic being allocated on the document your useEffect never touched, which is why our LLM SEO agency practice now audits initial HTML as a first-class deliverable rather than a technical footnote.
Move the fetch to the server. If a component genuinely needs client data, render a server-side version of the same content underneath it.
React Server Components, Streaming SSR and Edge Rendering
React Server Components changed the default. An RSC executes on the server, never ships its JavaScript to the browser, and emits markup that is complete on arrival. For content pages this is the strongest position React has ever offered: full HTML, minimal bundle, no hydration cost on the parts that do not need interactivity.
Streaming SSR sends HTML in chunks as it becomes ready, so the shell and the primary content reach the browser while slower fragments are still resolving. Suspense boundaries mark the seams. The measurable win is largest contentful paint, because the important markup no longer waits behind the slowest query on the page.
Edge rendering moves execution to a CDN point of presence near the user. Vercel and Netlify both offer it, and it recovers most of the TTFB that server rendering costs. Combine the three and a React app serves complete HTML, fast, globally. Getting there on a large estate is a sequencing problem more than a coding one, which is the kind of engagement our enterprise SEO agency team plans route by route.
Talk to ProStar SEO About Your React Rendering Strategy
We map every template on your site to a rendering strategy and a reason. Then we tell you which three changes recover the most indexed URLs, and what the work costs before you commit to anything.
Hydration Is Where React SEO Quietly Fails
Hydration is the step nobody audits. The server sends HTML, the browser downloads the same components as JavaScript, and React walks the existing DOM attaching event handlers so the markup becomes interactive. Done well, the user never notices. Done badly, it costs you a Core Web Vitals metric, and in one specific failure mode it costs you the content itself.
No page in the top ten results for this query gives hydration its own section. That is a gap, not a consensus.
What Hydration Costs in User Experience and Interaction to Next Paint (INP)
Interaction to next paint replaced first input delay as a Core Web Vitals metric, and it measures the full latency from a user’s tap to the next frame painted. Hydration competes directly with that. While React is attaching handlers across a large tree, the main thread is busy, and a tap on a button that is visibly on screen does nothing until the work finishes.
There is a second cost that is easier to miss. Between the server HTML painting and hydration completing, the page looks finished and behaves like a photograph. Users tap. Nothing happens. They tap again, which registers as a rage click in your analytics and as a long interaction in your field data.
The size of the penalty tracks the size of the bundle being hydrated. A marketing page hydrating 400 KB of components to make one navigation menu interactive is paying INP for nothing.
Measure it with real users rather than a lab run. The Chrome UX Report holds field data for your origin, and PageSpeed Insights surfaces it beside the synthetic score. Lab numbers are optimistic about hydration because the test machine is faster than your visitors’ phones.
React Hydration Mismatch: When the Server and the Client Disagree
A hydration mismatch happens when the markup React produces in the browser does not match what the server sent. React logs a warning, discards the server HTML for that subtree, and re-renders it client-side. The visible result is often nothing at all. The SEO result can be that content present in the initial HTML is replaced by different content, or by an error boundary, after the crawler has already read it.
Timestamps rendered with Date.now(), locale formatting that differs between server and browser, feature flags evaluated on the client, and personalisation applied after mount all produce mismatches. Redux stores rehydrated from local storage are another reliable source, as is any component that reads window during its first render. They are easy to introduce and easy to miss, because they usually do not break anything a human notices.
Check the browser console on a server-rendered route. A hydration warning on a page you expect to rank is a defect worth a ticket.
Partial Hydration, Islands Architecture and Astro
Partial hydration hydrates only the components that need interactivity and leaves the rest as static HTML. Islands architecture is the same idea as a design principle: the page is static by default, with small interactive islands that ship their own JavaScript.
Astro implements this natively and runs React components inside it, alongside the other framework-agnostic pieces of a modern stack, so an existing component library can move across without a rewrite. Next.js reaches a similar place through server components, and the practical effect on both is the same: less JavaScript to parse, fewer handlers to attach, lower INP, and markup that stays exactly as the crawler received it.
Static by default, interactive by exception. That ordering is what modern React SEO looks like.
Dynamic Rendering React SEO: the Fallback Google Calls a Workaround
Dynamic rendering detects the user agent and serves prerendered HTML to bots while sending the normal JavaScript app to humans. Google introduced the pattern in 2018, recommended it for a while, and has since labelled it a workaround rather than a long-term solution.
It still works. It is still the fastest way to stop the bleeding on a large CSR site while a real migration is scoped. Treat it as a tourniquet.
How Prerender.io, Rendertron and Puppeteer Serve Bots a Different Page
Prerender.io runs the managed version: middleware in your stack routes bot requests to their service, which returns a cached, fully rendered snapshot. Rendertron, Google’s own open-source implementation, does the same job on infrastructure you host. Both run Puppeteer underneath, driving a real browser with no interface, which loads the URL, waits for the network to settle, and serialises the resulting DOM.
The mechanism is user-agent detection, which is why the implementation detail matters more than the tool. Serve bots the same content humans get, rendered earlier. Serve them different content and you are cloaking, and the penalty for that is not a ranking dip.
- Request arrives, middleware inspects the user agent
- Bot requests route to the prerender service or your own Rendertron instance
- The service returns cached HTML, typically within a stale window you configure
- Human requests pass straight through to the React app
When Prerendering Is the Right Call and When It Is Technical Debt
Prerendering is the right call in three situations: a legacy SPA nobody will fund a rewrite for, a launch deadline that lands before a migration can, and a long tail of URLs whose value does not justify server rendering. In each case it buys indexation now at the cost of an extra system to keep alive.
It becomes debt the moment it stops being temporary. The cache goes stale, the user-agent list falls behind, the snapshot service goes down at 3 a.m. and nobody notices because humans still see a working site. Meanwhile every content change has to propagate through two rendering paths instead of one.
Two operational details decide whether it stays manageable. Set the cache window deliberately, because a snapshot service holding week-old HTML will happily serve Google a price you no longer charge. And monitor the bot path separately from the human path, since a broken prerender service is invisible to every human on your team while quietly removing your content from the index.
If the same engineering hours would move your highest-value templates to server rendering, spend them there instead.
React JS and SEO: Meta Tags, Titles and Structured Data
React JS and SEO meet most visibly in the document head. Title tags, meta descriptions, canonical tags and structured data all live outside the React root, which means something has to write them per route, on the server, before the response is sent.
Getting this wrong is common and cheap to fix. Getting it right is worth doing early, because it affects click-through rate on rankings you already hold.
React Helmet, react-helmet-async and the Next.js Metadata API
React Helmet was the standard answer for years and still ships in a large share of production apps. It has a known problem in server-rendered environments: the original library uses module-level state, which leaks between concurrent requests. react-helmet-async exists specifically to fix that, and any SSR app using the original should migrate.
The Next.js Metadata API supersedes both inside Next. Export a metadata object or a generateMetadata function from a route and the framework writes the head on the server, per route, with no client library involved.
export async function generateMetadata({ params }) {
const product = await getProduct(params.slug)
return {
title: `${product.name} | Example`,
description: product.summary,
alternates: { canonical: `https://example.com/products/${params.slug}` }
}
}
Canonical Tags, Open Graph and Twitter Cards in a Single Page Application
A canonical URL in a single page application has to update on every route change, and it has to be correct in the server response, not only after navigation. A self-referencing canonical on every indexable route is the safe default, and query-parameter variants should canonicalise to the clean path.
Open Graph and Twitter Cards control how the URL renders when someone shares it. Facebook, LinkedIn and X do not execute JavaScript when they scrape, so Open Graph markup injected client-side simply does not exist as far as those crawlers are concerned. This is the clearest possible demonstration of the initial-HTML principle, and it is easy to test: paste the URL into any social debugger and see what comes back.
Schema Markup and JSON-LD That Survives Hydration
Structured data gives Google an explicit statement of what a page is about, and rich results follow from it in the SERP. Schema markup in React should be emitted as JSON-LD in the server response, not appended to the head after mount.
The pattern that survives hydration is a script tag rendered as part of the server output with dangerouslySetInnerHTML, containing a schema.org graph built from the same data the page renders. Because it comes from one source, the markup cannot drift from the visible content, which is the condition Google’s structured data policy actually requires.
Validate with the Rich Results Test on the live URL rather than on pasted code. Pasted code tests your JSON. The live URL tests your rendering.
SEO in React JS Routing: URLs, the History API and Crawlable Links
SEO in React JS routing comes down to one requirement: every indexable view needs a real URL that returns real HTML when requested directly. Client-side routing gives users instant navigation. It gives crawlers nothing unless the server agrees to answer for those paths too.
Three things break here, and all three are cheap to prevent.
Hash Routing Is the Fastest Way to Lose Search Engine Indexation
Hash routing puts the route after a #, as in example.com/#/products/widget. Browsers never send the fragment to the server, so every route resolves to the same document and Google treats the whole app as one URL. Indexation collapses to a single page no matter how much content sits behind the router.
HashRouter still ships in tutorials because it works without server configuration. That convenience is the trap.
Use BrowserRouter, which is built on the HTML5 History API and produces clean paths the server can answer for. If your app is already on hash routes, the migration is a router swap plus a server rewrite rule, and it is usually the single highest-return change on a legacy React SPA.
React Router, Link Components and JavaScript-Only Navigation
React Router renders navigation through its Link component, which produces a genuine anchor element with a real href. That matters more than it sounds. A div with an onClick handler navigates fine for a mouse user and is invisible to a crawler, because Google follows href attributes and does not click things speculatively.
Audit for this directly. Crawl the site with Screaming Frog in text-only mode and compare the URLs discovered against your sitemap. Anything reachable only by JavaScript will be missing, and that gap is your internal linking loss.
- Every navigational element renders as
<a href="/real/path"> - Buttons trigger actions, anchors trigger navigation, and the two never swap roles
- Pagination and faceted navigation expose crawlable hrefs, not click handlers
- Infinite scroll ships a paginated fallback with real links
Server Configuration, HTTP Status Codes and Soft 404s
A React app on BrowserRouter needs the server to return the app shell for any path the router owns. A sitemap.xml listing those paths is what tells Google they exist in the first place, and robots.txt must stay out of the way of your JavaScript bundles: a blocked script is a page Googlebot cannot render.
That rewrite rule is standard, and it carries a specific hazard. Configured naively, the server returns HTTP status code 200 for every path, including ones that do not exist.
Google calls that a soft 404, and a site emitting thousands of them wastes crawl budget on URLs that should have been rejected at the door. Genuine missing routes must return a real 404 from the server. Moved routes must return a 301 redirect at the HTTP level, not a client-side redirect that fires after the bundle loads.
Next.js and Remix both expose this properly through route-level status handling. On a custom Express setup you own it yourself.
React SEO Optimization for Core Web Vitals
React SEO optimization for Core Web Vitals is mostly bundle work. React’s performance profile is dominated by how much JavaScript you ship and when it executes, and every Core Web Vitals metric responds to reducing that.
None of this rescues a page with empty initial HTML. Fix rendering first, then come here.
LCP, CLS and INP Inside a React Bundle
Largest contentful paint measures the moment the main content appears. In a CSR app, LCP cannot fire until the bundle has downloaded, parsed and rendered, which puts a floor under LCP that no amount of image work removes. Server rendering lowers that floor immediately.
Cumulative layout shift comes from content that arrives late and pushes down what is already on screen. React causes it in predictable ways: skeleton loaders replaced by taller content, images without explicit dimensions, banners injected after mount, and fonts swapping. Reserve the space before the content arrives and CLS falls to near zero.
INP is the hydration story from earlier plus long tasks: heavy re-renders, expensive context updates, and state changes that cascade through a large tree.
Set budgets rather than chasing a score. Google’s thresholds are 2.5 seconds for LCP, 0.1 for CLS and 200 milliseconds for INP, measured at the 75th percentile of real visits. Attach those numbers to templates, not to the site average. Core Web Vitals are scored per URL, and Google groups URLs by similarity when field data is thin. A homepage passing comfortably will mask a product template failing badly, and the product template is the one that sells.
Code Splitting, Lazy Loading and Bundle Size
Bundle size is the root cause behind most React Core Web Vitals failures, and code splitting is the direct treatment. Split by route first, so a visitor landing on one page downloads that page’s code rather than the entire application. React.lazy and Suspense handle the component boundary; Webpack and Vite handle the chunking.
Lazy loading has one rule that matters for SEO: never lazy-load content above the fold. Deferring the hero image or the main heading pushes LCP later and can hide the content from a crawler that does not scroll.
Audit the bundle before optimising it. A bundle analyser usually finds one date library, one icon set imported whole, and one dependency nobody remembers adding.
Image Optimization, Preload and Time to First Byte
Image optimization in React means correct formats, correct dimensions, and correct loading priority. Serve WebP or AVIF with a fallback, set explicit width and height on every image so the browser reserves space, and give every meaningful image alt text that describes it.
Preload the LCP image and the fonts the opening render depends on. Next.js Image does much of this automatically, including priority hints and responsive sizing.
Time to first byte is the metric server rendering puts at risk and a CDN puts back. Cache rendered HTML at the edge, keep the origin doing as little per request as possible, and TTFB stops being the argument against SSR.
Have ProStar SEO Measure Your React Core Web Vitals
Field data, not lab scores. We pull your Chrome UX Report numbers by template and show which React components are costing you INP.
SEO for React Apps Behind a Login or Serving Personalized Content
SEO for React apps gets complicated when part of the product sits behind authentication or changes per visitor. The instinct is to render everything and let Google sort it out. That produces duplicate content, wasted crawl budget, and occasionally an indexed page containing someone’s account data.
Decide per route what the public version of the page is, then render that version to everyone who is not signed in.
Authenticated Routes, Noindex and Gated Content
Authenticated routes should return a robots directive of noindex and, better still, never render a signed-in view to an unauthenticated request. Googlebot arrives signed out, so whatever your app shows a logged-out visitor is what gets indexed.
Gated content needs a real public layer. A page whose entire body is a login prompt has almost nothing to rank, and it will not hold a position regardless of what the gated material behind it is worth. Publish a substantive public version and gate the depth, which is the pattern our SEO content writing services team builds for subscription products.
Personalization, Duplicate Content and Canonical URLs
Personalisation creates near-identical URLs at scale: the same page with a session parameter, a referral tag, or a variant flag. Left alone, this is duplicate content competing with itself.
A self-referencing canonical URL on the clean path solves most of it. Personalised variants canonicalise to the base URL, tracking parameters are declared and ignored, and the version Google indexes stays the one you chose.
Multi-region React apps add hreflang on top, and it belongs in the server-rendered head like everything else in this section.
React SEO Best Practices, Ranked by Impact
React SEO best practices get published as flat checklists, which is why teams work through them in the wrong order and wonder why nothing moved. The list below is ordered by what actually changes indexation and rankings on the builds we audit.
Work down it. Do not start in the middle.
The Fixes That Move Indexation and Organic Traffic First
- Get content into the initial HTML. Server-render or statically generate every template you expect to rank. Everything else in this list is worth less than this one.
- Fix routing. Real URLs, real anchors, correct status codes. A page that cannot be reached cannot be ranked.
- Server-render the head. Titles, descriptions, canonicals and JSON-LD in the response, not after mount.
- Cut the bundle. Route-level code splitting, then dependency pruning.
- Then optimise images, preload and caching.
Indexation responds to the first three. Rankings respond to all five, plus the content itself and the links pointing at it. If crawl budget is your constraint, which it is on any React site above roughly 10,000 URLs, the first two items are the only ones that meaningfully move it.
The React JS SEO Checklist Before You Ship
Run this React JS SEO checklist against a staging URL before any release that touches rendering, routing or the head. It takes about twenty minutes and it catches the failures that are expensive to find in production.
- View Source contains the main content, not an empty root div
- Title tag, meta description and canonical are present in the raw response
- JSON-LD appears in the server output and validates
- Every navigational element is an anchor with an href
- Missing routes return 404, moved routes return 301
- No hydration warnings in the console on indexable routes
- Above-the-fold content is not lazy-loaded
- The rendered HTML in URL Inspection matches what a browser shows
Our full React SEO checklist goes further per template, and it is the artefact we hand clients at the end of an audit rather than a slide deck.
ReactJS SEO Tools: Search Console, Screaming Frog and Lighthouse
ReactJS SEO tools split into three jobs, and no single tool covers all three.
Google Search Console is the only source of truth for what Google actually did. The URL Inspection tool shows rendered HTML per URL, the Page Indexing report shows the discovered-not-indexed bucket where React sites accumulate losses, and the Rich Results Test validates structured data on the live page.
Screaming Frog crawls twice, once with JavaScript rendering enabled and once without, and the difference between those two crawls is a direct measurement of your rendering dependency. That comparison is the most useful single artefact in a React audit.
Lighthouse and PageSpeed Insights cover performance, with Lighthouse giving lab conditions and PageSpeed adding field data. Chrome DevTools with JavaScript disabled remains the fastest thirty-second sanity check anyone can run. For the reasoning behind any of it, web.dev and Google Search Central are the primary sources worth reading rather than summarising.
How to SEO React Apps Without a Full Rewrite
The reason React SEO problems persist for years is that the fix gets scoped as a rewrite, and rewrites do not get funded. They do not need to be. Rendering strategy is a per-route decision, so the migration can be per-route as well.
Start with the templates carrying commercial intent. Leave the dashboard alone.
Incremental Migration to Next.js, Remix or Astro
Next.js supports incremental adoption directly: run it alongside the existing app, move one route group at a time, and proxy the rest. Remix and Astro both accept existing React components, so the component library survives the move even when the routing layer does not. Each of the three keeps your components and replaces the layer above them.
A migration that works looks like this. Pick the ten templates that generate the most revenue. Move those to server rendering. Measure indexation and rankings for six weeks. Use the result to fund the next tranche.
Keep the URLs identical while you do it. A rendering migration that also changes paths mixes two experiments and you will not be able to tell which one moved the numbers, or which one broke them. Change the rendering. Hold everything else still. Server-side rendering is the variable you are testing, so let it be the only one. If paths genuinely have to change, run that as a separate release with its own redirect map, a month either side of the rendering work.
Headless WordPress with a React front end is the most common hybrid we see, and it fails in a specific way: the content team publishes into WordPress, the front end fetches it client-side, and nothing reaches the crawler. Our WordPress SEO services team fixes that at the fetch layer rather than the CMS.
A Real Pattern: an E-commerce SPA Moving from CSR to ISR
The pattern repeats across ecommerce SEO engagements. A SPA storefront renders category and product pages client-side. Search Console shows a large discovered-not-indexed bucket, and organic traffic sits well below what the catalogue size and link profile should support. CSR is the cause and it is visible in one View Source.
The fix moves product and category templates to ISR with a revalidation window matched to how often prices change, usually somewhere between fifteen minutes and a day. Product pages become static responses from the edge. The rendering queue leaves the critical path. Indexation follows crawl rather than trailing it by weeks, and search visibility starts tracking the catalogue instead of a fraction of it.
Timelines are honest rather than flattering: indexation shifts show up in weeks, ranking movement takes longer, and the size of the recovery depends on how much of the catalogue was missing to begin with.
Frequently Asked Questions About React SEO
Is React JS SEO friendly, or do I need Next.js?
React JS is SEO friendly when the HTML that leaves your server contains the content, and it is not when the HTML is empty. Next.js is the most direct way to guarantee the first case, which is why it is the common recommendation, but Remix, Astro and a well-built custom SSR setup all reach the same outcome. The framework is a means; the served HTML is the requirement.
What changes for SEO in ReactJS compared with a static site?
SEO in ReactJS adds one dependency a static site does not have: JavaScript execution between crawling and indexing. Everything else, content quality, links, site structure, works identically. Static site generation removes the dependency entirely by producing the same kind of files a static site would.
Can Googlebot index a React single page application without SSR?
Googlebot can index a React single page application without SSR, and frequently does. The catch is reliability rather than capability: rendering is queued, it is not guaranteed for every URL, and it consumes crawl budget you could spend on discovery. Small sites often get away with it. Large catalogues consistently do not.
Does React hurt Core Web Vitals?
React does not hurt Core Web Vitals by itself; large JavaScript bundles do, and React applications tend to have large bundles. INP is the metric most exposed, because hydration and re-renders occupy the main thread. A server-rendered React app with route-level code splitting can score as well as any stack.
Do I still need dynamic rendering if I already run SSR?
No. Dynamic rendering exists to compensate for missing server HTML, and SSR already provides it. Running both adds a second rendering path to maintain and a second place for content to drift out of sync. Google has been explicit that dynamic rendering is a workaround, not an architecture.
How long after a rendering fix do rankings actually move?
Recrawling and reindexing at scale takes weeks, not days, and ranking movement follows reindexation rather than accompanying it. On the migrations we run, the lag before reindexing shortens first, and it shows up in Search Console within two to four weeks, while meaningful search visibility change lands closer to the three-month mark. Anyone promising faster is describing a small site or guessing.
Work with a React SEO Agency That Reads the Source, Not the Screenshot
Most SEO agencies audit React sites in a browser, which shows them the rendered DOM and hides the entire problem. ProStar SEO audits the response. That distinction is why our recommendations for a React build look like engineering tickets rather than a list of keyword suggestions.
We work in regulated and technically difficult verticals where the margin for a vague deliverable is thin, and we do it month to month with no long-term contract.
What ProStar SEO Measures on a React Build
ProStar SEO measures four things on every React engagement. The raw HTML of every ranking template, compared against the rendered DOM. The gap between a JavaScript crawl and a text-only crawl. Field Core Web Vitals by template rather than site-wide. And the semantic coverage of the content itself, using the contextual density methodology we built for exactly this, because technical SEO and content are the same problem viewed from two ends.
We also report what we did not fix, and why. Some React defects cost more to repair than the traffic they block is worth, and a template serving forty visits a month does not justify a sprint. Saying so is part of the deliverable.
JavaScript SEO is not a separate discipline we bolt on. It is the part of technical SEO that decides whether the rest of the work is visible.
Book Your React SEO Consultation with ProStar SEO
Bring us a URL and we will tell you what Googlebot got. Month to month, no long-term contract, and you can cancel with thirty days’ notice.