Skip to content

Commit a09b50c

Browse files
authored
fix(solid-router): respect server flag during development SSR (#8254)
1 parent 79e938f commit a09b50c

4 files changed

Lines changed: 77 additions & 3 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@tanstack/solid-router': patch
3+
---
4+
5+
Fall back to the router instance's server flag during development SSR. This prevents router construction from entering client hydration on the server and restores provider-owned loading and match-state serialization.

packages/solid-router/src/RouterProvider.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,9 @@ export function RouterContextProvider<
4949
// registry priming in the Router constructor plus Transitioner's
5050
// settled-time load.
5151
const ready = Solid.createMemo(() =>
52-
isServer && !router._serverResult ? router.load().then(() => true) : true,
52+
(isServer ?? router.isServer) && !router._serverResult
53+
? router.load().then(() => true)
54+
: true,
5355
)
5456

5557
const OptionalWrapper = router.options.Wrap || SafeFragment
@@ -65,7 +67,7 @@ export function RouterContextProvider<
6567
// live. Runs after the load gate so matches are committed. The
6668
// client half — the hydration-claiming boot — runs at router
6769
// creation, outside the render.
68-
if (isServer) {
70+
if (isServer ?? router.isServer) {
6971
serializeMatchTransfer(router)
7072
}
7173
return children()

packages/solid-router/src/router.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ export class Router<
110110
// claiming walk. Inert without entries: an SPA page has no registry, and
111111
// a Start app transfers through its own channel (`router.serverSsr`),
112112
// so the first missing entry falls through to unchanged behavior.
113-
if (!isServer) {
113+
if (!(isServer ?? this.isServer)) {
114114
primeRouterFromRegistry(this)
115115
}
116116
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { describe, expect, it, vi } from 'vitest'
2+
import { renderToStream } from '@solidjs/web'
3+
import { isServer } from '@tanstack/router-core/isServer'
4+
import {
5+
RouterProvider,
6+
createMemoryHistory,
7+
createRootRoute,
8+
createRoute,
9+
createRouter,
10+
} from '../../src'
11+
12+
vi.mock('@tanstack/router-core/isServer', async (importOriginal) => ({
13+
...(await importOriginal()),
14+
isServer: undefined,
15+
}))
16+
17+
describe('RouterProvider (development SSR)', () => {
18+
it('creates a server router before request history is attached', () => {
19+
expect(isServer).toBeUndefined()
20+
21+
const router = createRouter({ routeTree: createRootRoute() })
22+
23+
expect(router.isServer).toBe(true)
24+
const history = createMemoryHistory({ initialEntries: ['/'] })
25+
router.update({ history })
26+
expect(router.history).toBe(history)
27+
expect(router.stores.matches.get()).toEqual([])
28+
})
29+
30+
it.each([false, true])(
31+
'loads once and serializes matches when preloaded is %s',
32+
async (preloaded) => {
33+
expect(isServer).toBeUndefined()
34+
35+
const loader = vi.fn(() => Promise.resolve('server-loader-data'))
36+
const rootRoute = createRootRoute()
37+
const indexRoute = createRoute({
38+
getParentRoute: () => rootRoute,
39+
path: '/',
40+
loader,
41+
component: () => <div>Index</div>,
42+
})
43+
const router = createRouter({
44+
routeTree: rootRoute.addChildren([indexRoute]),
45+
history: createMemoryHistory({ initialEntries: ['/'] }),
46+
isServer: true,
47+
})
48+
const loadSpy = vi.spyOn(router, 'load')
49+
50+
if (preloaded) {
51+
await router.load()
52+
}
53+
54+
const html = await renderToStream(() => (
55+
<RouterProvider router={router} />
56+
))
57+
58+
expect(loadSpy).toHaveBeenCalledTimes(1)
59+
expect(loader).toHaveBeenCalledTimes(1)
60+
expect(html).toContain('Index')
61+
expect(html).toContain('tsr:__root__')
62+
expect(html).toContain('tsr:/')
63+
expect(html).toContain('server-loader-data')
64+
loadSpy.mockRestore()
65+
},
66+
)
67+
})

0 commit comments

Comments
 (0)