Reading time: 11 minutes
React SEO means making every page of the app reach crawlers with text, links and metadata already in the HTML. In a React SPA built with Vite, SEO suffers because the server sends only a shell. The content appears after JavaScript runs. The fix is SSR, pre-rendering or a framework like Next.js.
In short
- React doesn’t hurt SEO, but the default SPA answers every URL with the same nearly empty HTML.
- Googlebot runs JavaScript, but in a 2024 study, the OpenAI, Anthropic and Perplexity crawlers read only the initial HTML.
- In general, pre-rendering public pages at build time with React Router fixes SEO without changing your stack.
- The official Next.js migration guide for Vite starts the React app as a SPA, so SEO doesn’t change right away.
Is React bad for SEO?
No. React can render HTML on the server, at build time or in the browser. The site’s architecture decides which. The risk, however, lies in the default SPA. According to the React documentation, build tools like Vite, Parcel and Rsbuild start with a client-only SPA. If you need SSR, you have to set it up on your own.
The risk comes from one detail: in a SPA, every route uses the same HTML file. In the Vite React template, for example, SEO depends on JavaScript. The index.html has a fixed title, with the project name, and an empty <div>:
<!-- part of the index.html of an app created with Vite's react-ts template -->
<head>
<title>my-app</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
For Google, this usually means a delay, not a block, because Googlebot runs JavaScript before indexing. Still, Google says server-side rendering or pre-rendering remains a great idea, since not all bots can run JavaScript.
AI crawlers, however, are a bigger concern. In the 2024 study by Vercel and MERJ, none of the major AI crawlers tested ran JavaScript. That included those from OpenAI, Anthropic and Perplexity. Gemini, on the other hand, rendered pages because it uses Googlebot’s infrastructure. Vercel, which maintains Next.js, sells hosting.
But on September 30, 2026, the owner of a Next.js site published a report on DEV Community. On his sites, OAI-SearchBot and GPTBot started rendering pages. He found no announcement from OpenAI, and the change is still unconfirmed.
So the SEO of a React SPA can do well on Google and still fail in AI answers. The guide on how to show up in Gemini, Perplexity and Claude shows which crawlers to allow for each one.

SSR, SSG or CSR: what changes for React SEO?
What changes is when the HTML gets its content. With CSR, that happens in the browser, while with SSR it happens on the server, on every request. With SSG, the HTML comes from the build. So a crawler without JavaScript gets the full page with SSR and SSG, but only a shell with CSR.
For React SEO, therefore, check what each setup sends in the first response. The guide on what Googlebot and AI crawlers see with SSR and CSR also covers ISR and streaming.
| Setup | Mode | What goes in the initial HTML |
|---|---|---|
| Vite React template | CSR | Shell with an empty <div id="root"> |
React Router SPA Mode (ssr: false) | CSR, with the root rendered at build | Only the root route layout |
React Router prerender | SSG | Full page for the listed URLs |
React Router SSR (ssr: true) | SSR | Full page on every request |
| Next.js App Router | SSG or SSR, per route | The page HTML, by default |
Next.js output: 'export' | SSG | One HTML file per route |
Under the hood, these frameworks use React’s own APIs. According to the React reference, the prerender function builds static HTML. It waits for all data to load before it finishes. In general, however, the framework calls these APIs, not your site’s code. The table and examples follow the docs for React 19, Vite 8, React Router 8 and Next.js 16.
Should you migrate a React SPA to Next.js for SEO?
Yes, especially when the site has many public pages and the team can rewrite routes and data fetching. On the other hand, if the app already uses React Router, framework mode fixes SEO with fewer changes. In both cases, however, the SEO gain only shows up when the React app’s public pages stop being a SPA.
The React documentation recommends starting new apps with a framework, such as Next.js or React Router. In addition, all the frameworks it recommends can build a SPA and static pages without a server. They also let you turn on SSR route by route.
In practice, the choice depends on the site:
- SPA with React Router and stable public pages: use framework mode with
prerender, because it keeps your components. Then the build creates the HTML. - Catalog or news that changes all day: use SSR in React Router or Next.js, since each request gets fresh HTML.
- Rewrite already planned: choose Next.js with the App Router, because it brings Server Components and the Metadata API.
- Only a logged-in area: keep the SPA, since crawlers don’t need those screens.
For the React team, server rendering helps beyond SEO:
“A common misunderstanding is that server rendering is only for SEO. While server rendering can improve SEO, it also improves performance by reducing the amount of JavaScript the user needs to download and parse before they can see the content on the screen.”
Matt Carroll and Ricky Hanlon, on the official React blog (2025)
With Next.js, the migration starts as a SPA
For the SEO of a React app coming from Vite, the official guide to migrating to Next.js has a catch. It starts by keeping the app as a SPA, without migrating the existing router. It also takes the main component out of pre-rendering. In fact, the guide itself ends by warning that the app doesn’t yet use most of Next.js’ benefits.
For crawlers, therefore, nothing changes until the routes move to the App Router. There, pages and layouts are Server Components by default, and the Metadata API generates the <head> tags on the server. After that, metadata and streaming need their own care for SEO in Next.js.
With React Router: SEO without leaving Vite
If you already use React Router, you can adopt framework mode and fix SEO without leaving Vite. The adoption guide swaps Vite’s React plugin for the React Router plugin. It starts with ssr: false, which means the app is still a SPA. The longest step is moving your routes into route modules. Then list your public pages in prerender, and the build creates an HTML file for each one:
// react-router.config.ts
import type { Config } from "@react-router/dev/config";
export default {
ssr: false, // no server: the build outputs static files
prerender: ["/", "/pricing", "/blog"], // URLs with ready-made HTML
} satisfies Config;
Other URLs stay in the SPA, which the build writes to a fallback file. With a server, however, ssr: true turns on SSR for the routes left out of prerender. In React Router 7, the same config has worked since version 7.2.
Does pre-rendering work for AI crawlers?
Yes, as long as pre-rendering happens at build time and serves the same HTML to every visit. For a crawler that reads only the first response, that page is the same as an SSR page. The limits, however, are in what stays out of the build and what changes after it.
- Data fetched in the browser: with React’s
prerender, only data that activates Suspense makes it in. So data fetched in an Effect, a common pattern in SPAs, stays out, as the React reference warns. In React Router, by contrast, routeloaderfunctions run at build time and bring the data into the HTML. - Dynamic routes: with
prerender: true, React Router builds only the static paths and leaves out URLs like/blog/:slug. So list the slugs in the config, as the pre-rendering guide shows. - Content that changes: prices, stock and news go stale between builds. In those cases, rebuild on every publish or use SSR.
For React SEO, build-time pre-rendering is the most predictable option, because everyone gets the same HTML. It also doesn’t depend on a list of crawlers.
What about pre-rendering as a service?
Pre-rendering as a service, however, works differently. A server detects the crawler, for example by its user agent, and sends it a rendered copy. People, meanwhile, get the SPA. Google calls this dynamic rendering and treats it as a temporary workaround. In addition, the technique only helps the crawlers on the service’s list.
That is the case, for example, with older Lovable sites, built with React and Vite. On Lovable’s own hosting, the copy goes only to verified crawlers. The cost and setup of pre-rendering as a service depend on the vendor and the size of the site.
How do you test React SEO after the change?
Compare the server’s first response with the page the browser builds, route by route. Once the deploy finishes, check each public URL. Its HTML needs the main text, its own title and links with href. Then run the test with an AI crawler’s user agent and check Search Console.
The command below downloads the page with the user agent OpenAI publishes for OAI-SearchBot, the crawler behind ChatGPT search. OpenAI warns that the version in the user agent may change. The command shows the status and the title. It also counts the HTML lines with a phrase from the text and with the empty shell. On Windows, run it in Git Bash.
URL="https://www.example.com/pricing/"
UA="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36; compatible; OAI-SearchBot/1.4; +https://openai.com/searchbot"
curl -sL -A "$UA" -o page.html -w "status: %{http_code}\n" "$URL"
grep -o "<title[^>]*>[^<]*</title>" page.html
grep -c "a phrase from the main text" page.html
grep -c '<div id="root"></div>' page.html
How do you read the test result?
For React SEO, repeat the test on three kinds of routes: the home page, a content page and a dynamic route. Read the result like this:
- Phrase at 0 and shell at 1: the route is still CSR. So check that it’s in
prerenderand that the new build went live. - The same title on every route: each route’s title still comes from the browser. A crawler without JavaScript sees only the default title.
- Status 200 on a URL that doesn’t exist: the SPA fallback answers every URL. Google may then treat the page as a soft 404, so the server should return 404.
The curl request comes from your IP, so it doesn’t reproduce IP blocks in a firewall. It also doesn’t get the copy that a pre-rendering service sends only to verified crawlers. For Google, on the other hand, the URL Inspection tool in Search Console shows how Google processed the page. The JavaScript SEO checklist covers the full routine, with logs and symptoms. This test is one part of a technical SEO audit.
Finally, rendering is only the technical base of the SEO that supports brand visibility on Google and in AI.
Frequently asked questions
Not always. Since React 19, any component can render <title> and <meta>, and React moves those tags into the <head>. In a SPA, however, React adds them in the browser, so a crawler without JavaScript sees the title from index.html. Still, metadata libraries help you change tags per route.
Not by default. The Vite React template creates a client-only SPA, the hardest case for SEO. Vite has an SSR API, but its docs describe it as low-level and aimed at library and framework authors. So, for an app, the shortest path is a framework built on Vite, such as React Router.
No. In February 2025, the React team deprecated Create React App for new apps. Instead, the team points to a framework or a build tool, such as Vite. For apps that already use CRA, the migration guides the team lists lead first to a client-only SPA. Therefore, the SEO gain comes later, route by route.
Main sources
- React, Creating a React App, with the recommended frameworks (2026).
- React, Build a React App from Scratch, on SPA, SSR and SSG with build tools (2026).
- React, reference for the prerender function in react-dom/static (2026).
- React, Sunsetting Create React App (2025).
- React Router, Pre-Rendering, with the ssr and prerender options (2026).
- Vite, Server-Side Rendering, with the pre-rendering section (2026).
- Next.js, How to migrate from Vite to Next.js (2026).
- Vercel, The rise of the AI crawler, study with MERJ (2024).


