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
- Return/throw a redirect from the loader or action instead
- Gate imperative navigation behind user events or useEffect so it never runs during SSR
- 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
- Use loader redirects for URL cleanup visible to SSR
- Keep { replace: true } navigation in event handlers/effects only
- Test components with renderToString to catch server-side navigation early
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
- You cannot use navigator.push() on the server because it is
- You cannot use navigator.go() on the server because it is a
- You cannot use navigator.back() on the server because it is
- You cannot use navigator.forward() on the server because it
- Prerender (data): Received a ${response.status} status code
AI-assisted analysis of remix-run/react-router@7aea711dd1 (2026-08-18).
Data as JSON: /api/errors/b97f78fc8a264552.
Report an issue: GitHub.