Summary
ssr: false routes with a pendingComponent still fail during dev hydration in the Solid Start selective SSR example.
This looks closely related to #7085 / #7266. #7266 fixed the ssr: 'data-only' case, but the same class of failure still reproduces for ssr: false in dev mode.
Reproduction
MRE branch:
https://github.com/ljho01/router/tree/codex/solid-ssr-false-pending-mre
Relevant added route:
https://github.com/ljho01/router/blob/codex/solid-ssr-false-pending-mre/e2e/solid-start/selective-ssr/src/routes/ssr-false-pending-component.tsx
The route is intentionally minimal:
export const Route = createFileRoute('/ssr-false-pending-component')({
ssr: false,
loader: async () => {
await new Promise((resolve) => setTimeout(resolve, 1500))
return { loadedAt: new Date().toISOString() }
},
pendingComponent: () => (
<div data-testid="ssr-false-pending-component-pending" />
),
component: SsrFalsePendingComponentRoute,
})
I tested against upstream main at d6decca41807e9ca28279e2db6640e7a8bdc1229.
Steps
pnpm install --frozen-lockfile
VITE_SERVER_PORT=57731 pnpm --dir e2e/solid-start/selective-ssr dev:e2e --host localhost --port 57731 --strictPort
Then open:
http://localhost:57731/ssr-false-pending-component
Actual behavior
The route never reaches the loaded component. It stays around the root HTML and the browser console reports:
Warning: The following error wasn't caught by any route! At the very least, consider setting an 'errorComponent' in your RootRoute!
Warning: template is not a function
In my Playwright check, the pending node was present but the ready label timed out after 10s:
{
"pending": 1,
"readyText": "TIMEOUT: locator.textContent: Timeout 10000ms exceeded.",
"bodyText": "Selective SSR E2E Test\nHome\nroot\nssr: \"undefined\"\nexpected data location execution:\nloader: server\ncontext: server"
}
The existing ssr: 'data-only' route in the same example works correctly under the same dev server.
Expected behavior
The ssr: false route should behave like the fixed ssr: 'data-only' case: hydrate without adding a mismatched outer pending fallback and eventually render the loaded component with no browser errors.
Extra notes
The production e2e target passes, so this appears to be dev-mode specific:
CI=1 NX_DAEMON=false pnpm nx run tanstack-solid-start-e2e-selective-ssr:test:e2e --outputStyle=stream --skipRemoteCache -- tests/pending-component-hydration.spec.ts
# 2 passed
The suspicious branch is in packages/solid-router/src/Match.tsx:
const resolvedNoSsr =
currentMatchState().ssr === false ||
currentMatchState().ssr === 'data-only'
const shouldSkipSuspenseFallback =
(isServer ?? router.isServer)
? resolvedNoSsr
: currentMatchState().ssr === 'data-only'
Locally, changing the client-side condition to also use resolvedNoSsr made both the data-only and ssr:false MRE routes pass in dev:
const shouldSkipSuspenseFallback = resolvedNoSsr
I did not include that fix in the MRE branch; the branch is left failing for reproduction.
Environment
- OS: macOS
- Node: v22.17.1
- pnpm: 10.28.0
- Browser check: Playwright Chromium
Summary
ssr: falseroutes with apendingComponentstill fail during dev hydration in the Solid Start selective SSR example.This looks closely related to #7085 / #7266. #7266 fixed the
ssr: 'data-only'case, but the same class of failure still reproduces forssr: falsein dev mode.Reproduction
MRE branch:
https://github.com/ljho01/router/tree/codex/solid-ssr-false-pending-mre
Relevant added route:
https://github.com/ljho01/router/blob/codex/solid-ssr-false-pending-mre/e2e/solid-start/selective-ssr/src/routes/ssr-false-pending-component.tsx
The route is intentionally minimal:
I tested against upstream main at
d6decca41807e9ca28279e2db6640e7a8bdc1229.Steps
Then open:
Actual behavior
The route never reaches the loaded component. It stays around the root HTML and the browser console reports:
In my Playwright check, the pending node was present but the ready label timed out after 10s:
{ "pending": 1, "readyText": "TIMEOUT: locator.textContent: Timeout 10000ms exceeded.", "bodyText": "Selective SSR E2E Test\nHome\nroot\nssr: \"undefined\"\nexpected data location execution:\nloader: server\ncontext: server" }The existing
ssr: 'data-only'route in the same example works correctly under the same dev server.Expected behavior
The
ssr: falseroute should behave like the fixedssr: 'data-only'case: hydrate without adding a mismatched outer pending fallback and eventually render the loaded component with no browser errors.Extra notes
The production e2e target passes, so this appears to be dev-mode specific:
CI=1 NX_DAEMON=false pnpm nx run tanstack-solid-start-e2e-selective-ssr:test:e2e --outputStyle=stream --skipRemoteCache -- tests/pending-component-hydration.spec.ts # 2 passedThe suspicious branch is in
packages/solid-router/src/Match.tsx:Locally, changing the client-side condition to also use
resolvedNoSsrmade both thedata-onlyandssr:falseMRE routes pass in dev:I did not include that fix in the MRE branch; the branch is left failing for reproduction.
Environment