remix-run/react-router · error
You must be using the SSR features of React Router in order
Error message
You must be using the SSR features of React Router in order to skip passing a `router` prop to `<RouterProvider>`
What it means
The framework-mode <RouterProvider> (hydrated-router) builds its router from window.__reactRouterContext, __reactRouterManifest, and __reactRouterRouteModules injected by the SSR build. initSsrInfo() finds none of them, so createHydratedRouter throws (lib/dom-export/hydrated-router.tsx89) when you omit the router prop outside that pipeline.
Source
Thrown at packages/react-router/lib/dom-export/hydrated-router.tsx:89
routeModules: window.__reactRouterRouteModules,
stateDecodingPromise: undefined,
router: undefined,
routerInitialized: false,
};
}
}
function createHydratedRouter({
getContext,
instrumentations,
}: {
getContext?: RouterInit["getContext"];
instrumentations?: ClientInstrumentation[];
}): DataRouter {
initSsrInfo();
if (!ssrInfo) {
throw new Error(
"You must be using the SSR features of React Router in order to skip " +
"passing a `router` prop to `<RouterProvider>`",
);
}
// We need to suspend until the initial state snapshot is decoded into
// window.__reactRouterContext.state
let localSsrInfo = ssrInfo;
// Note: `stateDecodingPromise` is not coupled to `router` - we'll reach this
// code potentially many times waiting for our state to arrive, but we'll
// then only get past here and create the `router` one time
if (!ssrInfo.stateDecodingPromise) {
let stream = ssrInfo.context.stream;
invariant(stream, "No stream found for single fetch decoding");
ssrInfo.context.stream = undefined;
ssrInfo.stateDecodingPromise = decodeViaTurboStream(stream, window)
.then((value) => {View on GitHub (pinned to 7aea711dd1)
Solutions
- Pass your own router: <RouterProvider router={createBrowserRouter(routes)} />
- Or stay in Framework Mode SSR so the build injects the hydration globals
- Do not reuse the generated entry.client.tsx in a non-framework setup; write your own root.tsx/entry
Example fix
// before
hydrateRoot(el, <RouterProvider />) // no SSR globals present
// after
const router = createBrowserRouter(routes)
hydrateRoot(el, <RouterProvider router={router} />) Defensive patterns
Strategy: validation
Validate before calling
// Only use the hydrated RouterProvider when the SSR build injected its globals
const hasFrameworkGlobals =
typeof window !== 'undefined' &&
Boolean(window.__reactRouterContext && window.__reactRouterManifest);
if (!hasFrameworkGlobals) {
root = <RouterProvider router={createBrowserRouter(routes)} />;
} Prevention
- Pass an explicit router prop in any non-framework/custom client setup
- Don't copy generated framework entry files into manually configured apps
- Smoke-render the app entry in CI for both framework and custom setups
When it happens
Trigger: Rendering the framework RouterProvider without a router prop in a manually-built client (Declarative/Data mode app reusing the framework entry); an entry.client.tsx copied into a non-framework setup; SSR pipeline disabled/removed so the hydration globals were never injected.
Common situations: Mixing framework-mode entry files into a custom Vite/webpack client; migrating an app off Framework Mode but keeping the generated entry.client.tsx; test harnesses rendering <RouterProvider /> without the SSR context.
Related errors
- The `routeDiscovery.mode` config cannot be set to "lazy" whe
- React Router presets must have a `name` property defined.
- Could not find package.json in ${rootDirectory} or any of it
- Unable to define routes with duplicate route id: "${id}"
- SPA Mode: Did you forget to include `<Scripts/>` in your roo
AI-assisted analysis of remix-run/react-router@7aea711dd1 (2026-08-18).
Data as JSON: /api/errors/1d1006a9a138ed2d.
Report an issue: GitHub.