remix-run/react-router · error
You cannot use router.${method}() on the server because it i
Error message
You cannot use router.${method}() on the server because it is a stateless environment What it means
createStaticRouter() (packages/react-router/lib/dom/server.tsx) returns a stateless server-side stub of the router API for SSR. It only supports reading state (state, routes, match, etc.); mutation/subscription methods like initialize(), subscribe(), navigate(), fetch(), and enableScrollRestoration() throw this error because a server request has no persistent browser session or history to track. The library throws it to fail fast when client-only router APIs are invoked during server rendering.
Source
Thrown at packages/react-router/lib/dom/server.tsx:447
revalidation: "idle" as RevalidationState,
fetchers: new Map(),
blockers: new Map(),
};
},
get routes() {
return dataRoutes;
},
get manifest() {
return manifest;
},
get window() {
return undefined;
},
match(locationArg) {
return matchRoutes(locationArg)?.map(mapRouteMatch) ?? null;
},
initialize() {
throw msg("initialize");
},
subscribe() {
throw msg("subscribe");
},
enableScrollRestoration() {
throw msg("enableScrollRestoration");
},
navigate() {
throw msg("navigate");
},
fetch() {
throw msg("fetch");
},
revalidate() {
throw msg("revalidate");
},
createHref,
encodeLocation,View on GitHub (pinned to 7aea711dd1)
Solutions
- Remove the call to router.initialize()/subscribe()/etc. from server-side code paths; the static router state is already fully initialized from the loader run — pass `router.state` to <StaticRouterProvider> instead.
- Guard environment: only call mutating router APIs when `typeof document !== 'undefined'` (or via the RouterProvider on the client), and skip them during SSR.
- For custom SSR entry points, use the documented flow: createStaticRouter() + createStaticHandler() + <StaticRouterProvider router={router} context={context}>, without manually initializing or subscribing.
- If you need server-side data loading, call staticHandler.query()/queryRoute() rather than router.navigate()/fetch().
Example fix
// before (entry.server.tsx)
const router = createStaticRouter(routes, context);
router.initialize(); // throws on the server
// after
const router = createStaticRouter(routes, context);
const contextValue = getStaticContextFromError(router, context, ...);
return <StaticRouterProvider router={router} context={contextValue} nonce={nonce} />; Defensive patterns
Strategy: validation
Validate before calling
const isServer = typeof document === 'undefined';
if (!isServer) {
router.initialize();
} Type guard
function isClientRouter(router: Router): boolean {
return typeof document !== 'undefined' && typeof router.initialize === 'function';
} Try / catch
try {
router.initialize();
} catch (e) {
if (e instanceof Error && e.message.includes('stateless environment')) {
// SSR: skip client-only router mutation; state is already populated
} else {
throw e;
}
} Prevention
- Never call router.initialize(), subscribe(), navigate(), or fetch() in code executed during SSR (entry.server.tsx, loaders, server-rendered components).
- Rely on <StaticRouterProvider> to hydrate server state; do not manually initialize a static router.
- Add an environment check (`typeof document !== 'undefined'`) around any shared client/server code that mutates router state.
- Keep client-only router logic inside effects/event handlers so it never runs during server render.
When it happens
Trigger: Calling router.initialize(), router.subscribe(), router.navigate(), router.fetch(), router.revalidate(), router.deleteFetcher(), router.enableScrollRestoration(), or any other mutating/subscription method on the router object returned by createStaticRouter() during SSR. Typically happens when code shared between client and server calls these methods without checking the environment, or when a static router handle leaks into server-side code.
Common situations: Calling router.initialize() manually in a custom entry point (entry.server.tsx) after createStaticRouter(); subscribing to router state changes in shared components/utilities rendered on the server; attempting fetcher calls (router.fetch()) or navigation during SSR; reusing a router-usage pattern from client-side createBrowserRouter in server rendering code.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- Prerender (data): Received a ${response.status} status code
- The `routeDiscovery.mode` config cannot be set to "lazy" whe
- A history only accepts one active listener
- Cannot write to a destroyed or ended writable stream
- You must be using the SSR features of React Router in order
AI-assisted analysis of remix-run/react-router@7aea711dd1 (2026-09-08).
Data as JSON: /api/errors/321f26c102fdf298.
Report an issue: GitHub.