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

  1. 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.
  2. Guard environment: only call mutating router APIs when `typeof document !== 'undefined'` (or via the RouterProvider on the client), and skip them during SSR.
  3. For custom SSR entry points, use the documented flow: createStaticRouter() + createStaticHandler() + <StaticRouterProvider router={router} context={context}>, without manually initializing or subscribing.
  4. 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

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


AI-assisted analysis of remix-run/react-router@7aea711dd1 (2026-09-08). Data as JSON: /api/errors/321f26c102fdf298. Report an issue: GitHub.