remix-run/react-router · error

You cannot use navigator.replace() on the server because it

Error message

You cannot use navigator.replace() on the server because it is a stateless environment. This error was probably triggered when you did a `navigate(${JSON.stringify(to)}, { replace: true })` somewhere in your app.

What it means

The stateless SSR navigator's replace() throws (lib/dom/server.tsx:275) for the same reason as push(): server rendering has no history to mutate. It fires when navigate(to, { replace: true }) executes on the server, and the message names the exact call.

Source

Thrown at packages/react-router/lib/dom/server.tsx:304

      serialized[key] = val;
    }
  }
  return serialized;
}

function getStatelessNavigator() {
  return {
    createHref,
    encodeLocation,
    push(to: To) {
      throw new Error(
        `You cannot use navigator.push() on the server because it is a stateless ` +
          `environment. This error was probably triggered when you did a ` +
          `\`navigate(${JSON.stringify(to)})\` somewhere in your app.`,
      );
    },
    replace(to: To) {
      throw new Error(
        `You cannot use navigator.replace() on the server because it is a stateless ` +
          `environment. This error was probably triggered when you did a ` +
          `\`navigate(${JSON.stringify(to)}, { replace: true })\` somewhere ` +
          `in your app.`,
      );
    },
    go(delta: number) {
      throw new Error(
        `You cannot use navigator.go() on the server because it is a stateless ` +
          `environment. This error was probably triggered when you did a ` +
          `\`navigate(${delta})\` somewhere in your app.`,
      );
    },
    back() {
      throw new Error(
        `You cannot use navigator.back() on the server because it is a stateless ` +
          `environment.`,
      );

View on GitHub (pinned to 7aea711dd1)

Solutions

  1. Return/throw a redirect from the loader or action instead
  2. Gate imperative navigation behind user events or useEffect so it never runs during SSR
  3. Move URL normalization (replace) into middleware or the loader where the Response is controlled

Example fix

// before
useEffect(() => {
  if (searchParams.get('ref')) navigate('/path', { replace: true })
}, [])
// still renders on server first render? move to loader:
export async function loader({ request }) {
  const url = new URL(request.url)
  if (url.searchParams.get('ref')) throw redirect('/path')
}
Defensive patterns

Strategy: validation

Validate before calling

const isServer = typeof window === 'undefined';
useEffect(() => {
  if (!isServer && needsReplace) navigate('/path', { replace: true });
}, []);

Prevention

When it happens

Trigger: navigate('/path', { replace: true }) executed during SSR render (component body, module init, or a server-side router.navigate call); post-form-submit navigation logic that runs during the server render pass.

Common situations: Replacing the URL after auth checks or cookie banners during render; form components that navigate on mount with replace; porting client-only logic into SSR'd components.

Related errors


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