Skip to content

fix(dev): use SPA fallback as index.html when public/index.html exists - #15453

Open
Cypher-Aura-19 wants to merge 1 commit into
remix-run:mainfrom
Cypher-Aura-19:fix/15444-spa-index-html
Open

fix(dev): use SPA fallback as index.html when public/index.html exists#15453
Cypher-Aura-19 wants to merge 1 commit into
remix-run:mainfrom
Cypher-Aura-19:fix/15444-spa-index-html

Conversation

@Cypher-Aura-19

@Cypher-Aura-19 Cypher-Aura-19 commented Aug 29, 2026

Copy link
Copy Markdown

What?

In SPA mode (ssr: false without prerendering /), always promote the generated SPA fallback to index.html, even when Vite has already copied a public/index.html into the client build directory.

Why?

Since the v8 Environment API migration, the SPA shell is first generated as __spa-fallback.html and later renamed to index.html.

When an application contains public/index.html, Vite copies that file into the output directory first. The existing finalize logic sees that index.html already exists and skips the rename, leaving the actual generated SPA shell in __spa-fallback.html.

Static hosts then serve the copied public/index.html instead of the React Router SPA shell.

This restores the previous SPA behavior while preserving the existing behavior when / itself is prerendered.

Testing

  • Added a regression test for ssr: false with public/index.html
  • Verified the generated index.html contains the complete SPA document and hydrates correctly
  • Verified the regression test fails without the production fix
  • vite-spa-mode: 46/46 passed
  • vite-prerender: 37/37 passed
  • vite-build: 16/16 passed
  • ESLint passed
  • Prettier passed
  • @react-router/dev typecheck passed
  • integration TypeScript check passed
  • change-file validation passed

Fixes #15444

Co-Authored-By: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

2 participants