remix-run/react-router · error
You cannot use navigator.push() on the server because it is
Error message
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. What it means
During SSR, the router runs on a stateless navigator whose push() throws (lib/dom/server.tsx:268): there is no history stack to push onto when rendering a response once. The message includes the offending destination so you can locate the navigate(to) call that ran on the server.
Source
Thrown at packages/react-router/lib/dom/server.tsx:297
...(val.name !== "Error"
? {
__subType: val.name,
}
: {}),
};
} else {
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.`,View on GitHub (pinned to 7aea711dd1)
Solutions
- Do the redirect in the loader/action: throw redirect('/login')
- Move navigate() calls into event handlers or effects so they only run client-side
- Replace render-time <Navigate>/navigate with loader-level redirects for SSR-visible conditions
Example fix
// before
function Dashboard() {
const navigate = useNavigate()
if (!user) navigate('/login') // runs during SSR render
}
// after
export async function loader({ request }) {
if (!await getUser(request)) throw redirect('/login')
} Defensive patterns
Strategy: validation
Validate before calling
const isServer = typeof window === 'undefined';
// Redirect decisions that must work during SSR belong in loaders:
export async function loader({ request }: Route.LoaderArgs) {
if (!(await getUser(request))) throw redirect('/login');
return null;
} Prevention
- Do redirects in loaders/actions with redirect(), never in component render
- Keep navigate() strictly inside event handlers and effects
- Assume any component can render on the server; guard browser-only logic
When it happens
Trigger: Calling navigate('/path') from useNavigate() during the server render of a component (not inside an effect/handler); invoking router.navigate() during SSR; navigation triggered synchronously in a component body or module scope while server rendering.
Common situations: Auth gating done by navigate() in a component instead of a loader redirect; login/onboarding flows calling navigate on mount during SSR; component-level side effects that assume a browser.
Related errors
- You cannot use navigator.replace() on the server because it
- 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/62aecee54f841012.
Report an issue: GitHub.