#15444·react-router

SPA mode (ssr: false) generates a broken index.html — real shell ends up in __spa-fallback.html instead

Author: ispradeepCreated Aug 27, 2026Updated Aug 31, 2026
Labelspkg:@react-router/devfeat:vitefeat:spa-mode

When building with ssr: false in react-router.config.ts, index.html in build/client comes out broken — it's just a handful of stray <link> tags with no <html>, <body>, or script tags at all. The actual working SPA shell (correct doctype, modulepreloads, <Scripts />, everything) gets written to build/client/__spa-fallback.html instead, which nothing serves by default.

This means any static host serving index.html for / (Caddy/nginx/S3/etc, anything not aware of __spa-fallback.html) just serves a blank page.

Repro:

// react-router.config.ts
export default {
  ssr: false,
} satisfies Config;
bun run build
# or: react-router build

Check build/client/index.html — it's ~300 bytes, only contains a few <link rel="icon">/<link rel="manifest"> tags from app/root.tsx's links() export. Compare to build/client/__spa-fallback.html in the same output — that one has the full correct shell.

I confirmed this is specifically tied to @react-router/[email protected] — bumping just the react-router package to 8.3.0 while keeping @react-router/[email protected] builds a correct index.html (still using v7's build path). Aligning @react-router/dev to 8.3.0 reproduces the broken output every time.

Versions:

  • react-router: 8.3.0
  • @react-router/dev: 8.3.0
  • vite: 7.3.6
  • node: v24.19.0, bun: 1.4.0

Expected: index.html should contain the full SPA shell (same content as __spa-fallback.html), like it did on 7.18.2.

Workaround: rolled back to [email protected] for now, where index.html builds correctly.