---
title: "Vite React SEO: prerender or SSG for Google and AI bots"
description: "Vite builds a client-only SPA by default. Four ways to prerender a Vite + React app, with config verified on 15 Oct 2026, plus the hosting fallback that undoes it."
url: https://katman.pro/vite-react-seo
lang: en
updated: 2026-10-18
---

Guide Updated 18 Oct 2026

# How do I make a Vite + React app readable by Google and AI bots?

> Vite isn’t bad for SEO, but its default React template ships an empty div and fills it in with JavaScript. Prerender your public routes at build time with React Router’s prerender option, vite-react-ssg, TanStack Start or Vike, give each route its own title, and make unknown URLs return a real 404. Then curl each route as a bot: the text should be in the HTML before any script runs.

## What Vite gives you by default

A fresh `npm create vite@latest -- --template react-ts` project builds one `index.html` with `<div id="root"></div>` and a script tag. Every route serves that file, and React draws the page in the browser. Googlebot renders it later from a queue; the crawlers behind ChatGPT and Claude did not run JavaScript in Vercel and MERJ’s 2024 log study, so they see the empty div. The background is on [Is React bad for SEO?](https://katman.pro/react-spa-seo-ai-crawlers) and [Do AI crawlers render JavaScript?](https://katman.pro/do-ai-crawlers-render-javascript).

Vite itself supports prerendering: its SSR guide says you can “pre-render these routes into static HTML using the same logic as production SSR”, but it ships a demo script, not a plugin ([Vite, “Server-Side Rendering”](https://vite.dev/guide/ssr), v8.3.4, read 15 Oct 2026). In practice you use a framework or plugin on top.

## Four ways to prerender (versions checked on npm, 15 Oct 2026)

| Option | Version | Fits when | Effort |
| --- | --- | --- | --- |
| React Router framework mode, `prerender` | react-router 8.4.0 | You already use React Router | Medium: move to framework mode (Vite plugin, `routes.ts`) |
| vite-react-ssg | 0.9.2 (15 Jul 2026) | React Router v6 data routes | Low: new entry and build command |
| TanStack Start, `prerender` | @tanstack/react-start 1.169.2 | New project, or a Lovable app upgraded to TanStack Start | Medium |
| Vike, `prerender: true` | vike 0.4.267 | You want control over the server and build | Medium |

### React Router

```ts
// react-router.config.ts
import type { Config } from "@react-router/dev/config";

export default {
  ssr: false,                      // no runtime server, static files only
  async prerender({ getStaticPaths }) {
    const slugs = await getPostSlugs();
    return [...getStaticPaths(), ...slugs.map((s) => `/blog/${s}`)];
  },
} satisfies Config;
```

`prerender: true` covers static paths only; dynamic segments like `/blog/:slug` need the list. The build writes `build/client/<path>/index.html` per route, plus a `__spa-fallback.html` for the rest when `/` is prerendered ([React Router, “Pre-Rendering”](https://reactrouter.com/how-to/pre-rendering), read 15 Oct 2026).

### vite-react-ssg

```ts
// src/main.tsx  (package.json: "build": "vite-react-ssg build")
import { ViteReactSSG } from "vite-react-ssg";
import { routes } from "./App";

export const createRoot = ViteReactSSG({ routes });
```

Dynamic routes add `getStaticPaths: () => ["nest/b1", "nest/b2"]`. Its README recommends React Router’s built-in prerendering for v7 and later and keeps itself for v6 users ([npm README](https://www.npmjs.com/package/vite-react-ssg), read 15 Oct 2026).

### TanStack Start and Vike

```ts
// vite.config.ts (TanStack Start)
import { defineConfig } from "vite";
import { tanstackStart } from "@tanstack/react-start/plugin/vite";
import viteReact from "@vitejs/plugin-react";

export default defineConfig({
  plugins: [tanstackStart({ prerender: { enabled: true, crawlLinks: true } }), viteReact()],
});
```

With `crawlLinks`, every page linked from a prerendered page is rendered too ([TanStack Start, “Static prerendering”](https://tanstack.com/start/latest/docs/framework/react/guide/static-prerendering), read 15 Oct 2026). In Vike, `export default { prerender: true }` in `pages/+config.js` writes the HTML to `dist/client/` ([Vike, “Pre-rendering”](https://vike.dev/pre-rendering), read 15 Oct 2026).

## Five steps, whichever tool you pick

1.  **List the public routes.** Home, pricing, features, docs, blog, situation pages. Leave `/app/*` client-rendered.
2.  **Put the title and description in each route.** With React 19 you can render `<title>` and `<meta name="description">` inside the component; React Router’s route docs now recommend that over its `meta` export. Prerendering puts them into each HTML file.
3.  **Fix the host’s fallback.** A rewrite of `/*` to `index.html` with a 200, or Cloudflare’s `not_found_handling: "single-page-application"`, answers every unknown path with your app. Serve prerendered files first and a real 404 page for the rest.
4.  **Use real links.** `<Link to>` renders an `<a href>`; `onClick={() => navigate(...)}` on a `div` doesn’t, and crawlers follow only real links.
5.  **Verify every route as a bot.** See the loop below.

## Check every route (example)

```bash
UA='Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko); compatible; GPTBot/1.4; +https://openai.com/gptbot'
for p in / /pricing /features /blog/first-post /does-not-exist; do
  printf "%-20s %s %5s words  %s\n" "$p" \
    "$(curl -s -o /dev/null -w '%{http_code}' -A "$UA" "https://yoursite.com$p")" \
    "$(curl -s -A "$UA" "https://yoursite.com$p" | sed 's/<[^>]*>/ /g' | wc -w)" \
    "$(curl -s -A "$UA" "https://yoursite.com$p" | grep -o '<title>[^<]*' | head -1)"
done
```

Pass: every real route returns 200 with hundreds of words and its own title; `/does-not-exist` returns 404. If all routes share one title, step 2 didn’t make it into the build. The user agent string is the one OpenAI lists today (GPTBot/1.4, [OpenAI crawlers](https://developers.openai.com/api/docs/bots), read 15 Oct 2026). If bots get 403 instead, the block is at your CDN: see [Cloudflare blocking AI crawlers](https://katman.pro/cloudflare-blocking-ai-crawlers).

## Where Katman fits, and where it doesn’t

Katman is an MCP server inside your AI coding tool (Cursor, Claude Code, VS Code, Windsurf and others). `katman_scan_code` detects Vite, the router and whether each page renders only in the browser; `katman_prompt` returns the rendering fix prompt with prerender options for a Vite single-page app and an acceptance check; `katman_audit_site` re-fetches the live routes as AI crawlers do and confirms the text, titles and 404s. Your coding tool writes the code; Katman doesn’t touch the repo by itself. Katman Pro is $99 a month for up to 3 sites, with fix prompts and page briefs uncapped ([pricing](https://katman.pro/pricing)). It can’t make an assistant recommend you; it makes sure the pages can be read and checks each fix. The step order is in [the Katman method](https://katman.pro/method).

Written by the Katman team, Rnify Limited. Vite, React Router, TanStack, Vike, OpenAI and npm pages read 15 October 2026.

## Who this is not for

-   Vite apps that are only a logged-in dashboard: prerendering adds nothing there.
-   Vue or Svelte projects on Vite: the idea is the same, the plugins differ.
-   Sites already on Next.js or Astro: see those guides instead.

## Frequently asked questions

### Is Vite bad for SEO?

No. Vite is a build tool; the SEO problem is the client-only single-page app its React template starts with. Add prerendering or server rendering and Vite output is plain HTML like any other site.

### Which prerender option should I pick for an existing Vite SPA?

If you already use React Router, its prerender option is the shortest path, after moving to framework mode. On React Router v6 data routes, vite-react-ssg fits. New projects can start on TanStack Start or Vike.

### Is react-snap still an option?

We wouldn’t start with it today: its latest npm release is from December 2018 (registry, read 15 Oct 2026). The maintained options in the table cover the same job.

### Vite vs Next.js for SEO: which is better?

Both can ship complete HTML. A prerendered Vite app and a statically generated Next.js page look the same to a crawler; pick Next.js when you need per-request server rendering, not for SEO alone.

### My Lovable app is React + Vite. Do I need any of this?

Check first. Lovable says older React + Vite apps get prerendered HTML for verified crawlers on its hosting, and apps created from 13 May 2026 use TanStack Start with server rendering. Self-hosted exports don’t get Lovable’s prerendering.

### Why do prerendered pages still show up as soft 404s?

Usually the host’s SPA fallback. A rewrite that answers every unknown path with index.html and a 200 turns typos and deleted pages into empty pages. Serve a 404 page with a 404 status instead.

## Related pages

-   [**Is React bad for SEO, or is it my single-page app?** React is fine for SEO; an empty SPA shell is not. Google renders JavaScript in a queue, the crawlers behind ChatGPT and Claude read only raw HTML. Test, then fix.](https://katman.pro/react-spa-seo-ai-crawlers)
-   [**Do AI crawlers like GPTBot and ClaudeBot render JavaScript?** In Vercel and MERJ’s Dec 2024 log study, GPTBot, ClaudeBot and PerplexityBot didn’t run JavaScript; Google and Apple did. What’s measured, what’s assumed, a test.](https://katman.pro/do-ai-crawlers-render-javascript)
-   [**Is Astro good for SEO and AI search, and what still needs setting up?** Astro ships plain HTML by default, so AI crawlers can read it. Still on you: the sitemap integration, robots.txt, llms.txt, JSON-LD and client:only islands.](https://katman.pro/astro-seo-ai-search)
-   [**I built my site with Lovable, Bolt or Cursor, and ChatGPT never mentions it** Built it with Lovable, Bolt, v0 or Cursor and ChatGPT never mentions it? Six traps hide vibe-coded sites; each has a one-minute test and a fix for your AI tool.](https://katman.pro/vibe-coded-site-invisible-to-chatgpt)
-   [**The Katman method: research, plan, build, verify** Four steps, three gates and 15 prompts with acceptance criteria that make a site readable, quotable and recommendable by ChatGPT, Claude, Gemini and Perplexity.](https://katman.pro/method)
-   [**Katman MCP** Katman MCP runs in Cursor, Claude Code and VS Code: it scans your code, audits your live site like AI crawlers do and checks every fix. Katman Pro from $99/month.](https://katman.pro/mcp)

Updated 18 October 2026 · Katman
