Description
A hydration regression seems to have been introduced for Solid when using ssr: 'data-only' together with a pendingComponent that returns any DOM element.
I reduced this to a very small MRE. The route shape is essentially:
export const Route = createFileRoute('/mre-data-only')({
ssr: 'data-only',
loader: async () => {
await new Promise((r) => setTimeout(r, 1500))
return 'OK'
},
pendingComponent: () => <div />,
component: () => <div>{Route.useLoaderData()}</div>,
})
On initial load / hard refresh of this route:
pendingComponent: () => <div /> fails
pendingComponent: () => <div>PENDING</div> fails
pendingComponent: () => <p>PENDING</p> fails
pendingComponent: () => 'PENDING' works
pendingComponent: () => null works
So the minimal failing case appears to be: pendingComponent returns a single DOM element.
Actual behavior
On initial load or refresh, the app hits hydration/runtime errors such as:
Hydration Mismatch. Unable to find DOM nodes for hydration key
TypeError: template is not a function
and the page can remain stuck in the pending state.
Expected behavior
For ssr: 'data-only', the route should hydrate cleanly and transition from pendingComponent to the loaded route component.
Version boundary
Working:
@tanstack/solid-start@1.166.18
@tanstack/solid-router@1.167.5
Broken:
@tanstack/solid-start@1.167.0
@tanstack/solid-router@1.168.0
Still broken on latest tested:
@tanstack/solid-start@1.167.15
@tanstack/solid-router@1.168.9
Suspected regression
The behavior change appears to line up with the Match.tsx refactor in commit 0545239 (refactor: signal based reactivity).
In the previously working version (@tanstack/solid-router@1.167.5), the outer suspense fallback was effectively disabled for resolvedNoSsr routes on both server and client:
(isServer ?? router.isServer) || resolvedNoSsr
? undefined
: <Dynamic component={resolvePendingComponent()} />
In @tanstack/solid-router@1.168.0, this became:
(isServer ?? router.isServer) && resolvedNoSsr
? undefined
: <Dynamic component={resolvePendingComponent()} />
For ssr: 'data-only', resolvedNoSsr is true, so this changes client behavior:
- old behavior: outer suspense fallback is also disabled on the client
- new behavior: outer suspense fallback is enabled on the client, but still disabled on the server
That seems to create the hydration mismatch.
Extra verification
I also monkey-patched the latest installed @tanstack/solid-router locally and changed only that condition back from && to ||.
With that one-line change:
- hydration succeeded
template is not a function disappeared
pendingComponent: () => <div /> worked again
- route completed from pending to
OK
Related
This may be related to #6824, but this MRE seems narrower: ssr: 'data-only' + pendingComponent returning any DOM element is enough to trigger it.
Description
A hydration regression seems to have been introduced for Solid when using
ssr: 'data-only'together with apendingComponentthat returns any DOM element.I reduced this to a very small MRE. The route shape is essentially:
On initial load / hard refresh of this route:
pendingComponent: () => <div />failspendingComponent: () => <div>PENDING</div>failspendingComponent: () => <p>PENDING</p>failspendingComponent: () => 'PENDING'workspendingComponent: () => nullworksSo the minimal failing case appears to be:
pendingComponentreturns a single DOM element.Actual behavior
On initial load or refresh, the app hits hydration/runtime errors such as:
Hydration Mismatch. Unable to find DOM nodes for hydration keyTypeError: template is not a functionand the page can remain stuck in the pending state.
Expected behavior
For
ssr: 'data-only', the route should hydrate cleanly and transition frompendingComponentto the loaded route component.Version boundary
Working:
@tanstack/solid-start@1.166.18@tanstack/solid-router@1.167.5Broken:
@tanstack/solid-start@1.167.0@tanstack/solid-router@1.168.0Still broken on latest tested:
@tanstack/solid-start@1.167.15@tanstack/solid-router@1.168.9Suspected regression
The behavior change appears to line up with the
Match.tsxrefactor in commit0545239(refactor: signal based reactivity).In the previously working version (
@tanstack/solid-router@1.167.5), the outer suspense fallback was effectively disabled forresolvedNoSsrroutes on both server and client:In
@tanstack/solid-router@1.168.0, this became:For
ssr: 'data-only',resolvedNoSsris true, so this changes client behavior:That seems to create the hydration mismatch.
Extra verification
I also monkey-patched the latest installed
@tanstack/solid-routerlocally and changed only that condition back from&&to||.With that one-line change:
template is not a functiondisappearedpendingComponent: () => <div />worked againOKRelated
This may be related to #6824, but this MRE seems narrower:
ssr: 'data-only'+pendingComponentreturning any DOM element is enough to trigger it.