Skip to content

fix: serve pages in SPA mode when the route also has an API handler (1.x) - #2355

Merged
brenelz merged 1 commit into
1.xfrom
fix/spa-page-with-api-handler-1x
Oct 14, 2026
Merged

brenelz merged 1 commit into
1.xfrom
fix/spa-page-with-api-handler-1x

Conversation

@lxsmnsyc

Copy link
Copy Markdown
Member

The 1.x version of #2354.

Problem

With ssr: false, a route file that exports both a page and an API handler, such as a [...404].tsx with a GET, makes every URL it matches return 404 instead of the SPA shell.

Cause

matchAPIRoute in router/routes.ts counted a route as a page only when it had a page component (route.page === true && route.$component !== undefined). SPA builds leave page components out of the server routes, so isPage was always false there. When the GET returned undefined, the handler hit if (!match.isPage) return; and the request got a 404.

Fix

Use route.page alone. With SSR, a route with a page always has its component, so SSR doesn't change.

The new spec needs vinxi/routes, which vinxi only serves inside an app build. vitest.config.ts aliases it to test/vinxi-routes.stub.ts, and the spec mocks that. The stub is outside src, so it isn't compiled into dist.

Verification

basic fixture with export const GET = () => undefined added to [...404].tsx, production builds:

Mode / /about /nope
ssr: false, before 404 404 404
ssr: false, after 200 200 200 (the client renders the not-found page)
  • New router/routes.spec.ts. The SPA case fails without the fix.
  • packages/start: 98 tests, typecheck and build pass.
  • apps/tests: the unit test passes. Cypress: 18 of 19 pass.
  • The one Cypress failure is server-function.cy.ts › "should reject the promise when the response is a 5xx without an X-Error header". It fails the same way on clean 1.x.

Not part of this PR: with SSR, /nope on that fixture returns 200 instead of 404, and nitro logs unhandledRejection: TypeError: Cannot read properties of undefined (reading 'file'). Clean 1.x does the same.

🤖 Generated with Claude Code

`matchAPIRoute` only counted a route as a page when it had a page
component. SPA builds leave page components out of the server routes, so a
route file exporting both a page and `GET` was treated as API-only. When
its `GET` returned nothing, the handler stopped there and the request got a
404. A catch-all like `[...404].tsx` with a `GET` turned every page into a
404.

Use `route.page` alone. SSR routes with a page always have the component,
so nothing changes there.

The new spec needs `vinxi/routes`, which vinxi only serves inside an app
build, so vitest aliases it to a stub the spec mocks.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Oct 14, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 44809c0

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@solidjs/start Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Oct 14, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@solidjs/start@2355

commit: 44809c0

@brenelz
brenelz merged commit 273496b into 1.x Oct 14, 2026
7 checks passed
@brenelz
brenelz deleted the fix/spa-page-with-api-handler-1x branch October 14, 2026 13:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants