Describe the bug
In Solid 2 SSR/hydration, a code-split route rendered through lazy() without an enclosing <Loading> boundary can leave the whole reactive system halted after hydration.
The server-rendered DOM remains visible, so the page looks hydrated, but signals and event-driven updates after the lazy route no longer run. The only observable diagnostic in our app was console.error; neither window.onerror nor unhandledrejection received an event, and there was no error value that the application could handle programmatically.
This is not a request to make lazy() work without a Loading boundary. The current lazy() contract documents that it suspends through an enclosing <Loading>, and the hydration error itself says to ensure that it is inside one. The problem is the failure mode when that invariant is missed: a contained, actionable hydration error would be much safer than a page that silently stops responding.
The issue was encountered through @solidjs/router's fileRoutes() adapter, which turns code-split route entries into Solid lazy() components. The router is only the trigger; the failure is in the Solid hydration/reactivity boundary.
Your Example Website or App
No public reproduction is available yet. The minimal shape is included below; the original reproduction was in a private Solid 2 SSR app.
import { lazy } from "solid-js";
import { createRouter } from "@solidjs/router";
const Page = lazy(() => import("./Page"), undefined, "/src/Page.tsx");
const Router = createRouter({ routes: [{ path: "/", component: Page }] });
// In an SSR app, mount the router outlet directly, without <Loading>:
<Router>{props => <>{props.children}</>}</Router>;
Steps to Reproduce the Bug or Issue
- Run a Solid 2 SSR application with a code-split route (
lazy() or fileRoutes() with code splitting enabled).
- Render the router's matched children directly, without an enclosing
<Loading> boundary.
- Load the route with SSR and let the browser hydrate it.
- Observe that the SSR markup remains visible and the console reports the missing lazy hydration preload /
Ensure it is inside a Loading boundary diagnostic.
- Trigger an unrelated signal update or event handler after hydration.
The route content stays on screen, but the reactive graph no longer processes subsequent updates. In our application there was no window.onerror or unhandledrejection event for the failure.
Expected behavior
If a lazy component is rendered outside the required <Loading> boundary, hydration should fail in a contained and observable way: for example, by reaching an ErrorBoundary, dispatching a browser-level error, or exposing a documented programmatic failure signal. The application should not appear hydrated while the global reactive system is silently halted.
Platform
- OS: macOS
- Browser: Chrome
solid-js: 2.0.0-rc.6
@solidjs/web: 2.0.0-rc.6
@solidjs/router: 2.0.0-next.21
@solidjs/vite-plugin: 3.0.0-next.39
Additional context
The application workaround is either to wrap the router outlet in <Loading fallback={null}> or to disable route code splitting. The root boundary has a separate streaming SSR trade-off: a catch-all 404 may commit as HTTP 200 after the shell flushes.
Related Solid 2 hydration/lazy fixes include #2860 and #3012, but this report is about the missing error signal and global reactive halt when the required boundary is absent.
Describe the bug
In Solid 2 SSR/hydration, a code-split route rendered through
lazy()without an enclosing<Loading>boundary can leave the whole reactive system halted after hydration.The server-rendered DOM remains visible, so the page looks hydrated, but signals and event-driven updates after the lazy route no longer run. The only observable diagnostic in our app was
console.error; neitherwindow.onerrornorunhandledrejectionreceived an event, and there was no error value that the application could handle programmatically.This is not a request to make
lazy()work without aLoadingboundary. The currentlazy()contract documents that it suspends through an enclosing<Loading>, and the hydration error itself says to ensure that it is inside one. The problem is the failure mode when that invariant is missed: a contained, actionable hydration error would be much safer than a page that silently stops responding.The issue was encountered through
@solidjs/router'sfileRoutes()adapter, which turns code-split route entries into Solidlazy()components. The router is only the trigger; the failure is in the Solid hydration/reactivity boundary.Your Example Website or App
No public reproduction is available yet. The minimal shape is included below; the original reproduction was in a private Solid 2 SSR app.
Steps to Reproduce the Bug or Issue
lazy()orfileRoutes()with code splitting enabled).<Loading>boundary.Ensure it is inside a Loading boundarydiagnostic.The route content stays on screen, but the reactive graph no longer processes subsequent updates. In our application there was no
window.onerrororunhandledrejectionevent for the failure.Expected behavior
If a lazy component is rendered outside the required
<Loading>boundary, hydration should fail in a contained and observable way: for example, by reaching anErrorBoundary, dispatching a browser-level error, or exposing a documented programmatic failure signal. The application should not appear hydrated while the global reactive system is silently halted.Platform
solid-js: 2.0.0-rc.6@solidjs/web: 2.0.0-rc.6@solidjs/router: 2.0.0-next.21@solidjs/vite-plugin: 3.0.0-next.39Additional context
The application workaround is either to wrap the router outlet in
<Loading fallback={null}>or to disable route code splitting. The root boundary has a separate streaming SSR trade-off: a catch-all 404 may commit as HTTP 200 after the shell flushes.Related Solid 2 hydration/lazy fixes include #2860 and #3012, but this report is about the missing error signal and global reactive halt when the required boundary is absent.