withastro/astro · warning · Error
The view transitions client API was called during a server s
Error message
The view transitions client API was called during a server side render. This may be unintentional as the navigate() function is expected to be called in response to user interactions. Please make sure that your usage is correct.
What it means
navigate() from the View Transitions client router is browser-only, but the router module can be imported during SSR (from component frontmatter, layouts, or shared utils). Calling it server-side does nothing except log this warning — an Error named 'Warning' instantiated for the stack trace, printed once thanks to the navigateOnServerWarned flag — then returns early.
Source
Thrown at packages/astro/src/transitions/router.ts:592
// This log doesn't make it worse than before, where we got error messages about uncaught exceptions, which can't be caught when the trigger was a click or history traversal.
// Needs more investigation on root causes if errors still occur sporadically
const err = e as Error;
// biome-ignore lint/suspicious/noConsole: allowed
console.log('[astro]', err.name, err.message, err.stack);
}
}
let navigateOnServerWarned = false;
export async function navigate(href: string, options?: Options) {
if (inBrowser === false) {
if (!navigateOnServerWarned) {
// instantiate an error for the stacktrace to show to user.
const warning = new Error(
'The view transitions client API was called during a server side render. This may be unintentional as the navigate() function is expected to be called in response to user interactions. Please make sure that your usage is correct.',
);
warning.name = 'Warning';
console.warn(warning);
navigateOnServerWarned = true;
}
return;
}
await transition('forward', originalLocation, new URL(href, location.href), options ?? {});
}
function onPopState(ev: PopStateEvent) {
if (!transitionEnabledOnThisPage() && ev.state) {
// The current page doesn't have View Transitions enabled
// but the page we navigate to does (because it set the state).
// Do a full page refresh to reload the client-side router from the new page.
location.reload();
return;
}
// History entries without state are created by the browser (e.g. for hash links)
// Our view transition entries always have state.View on GitHub (pinned to 52e6c34790)
Solutions
- Guard the call: if (typeof window !== 'undefined') navigate('/target')
- Move navigation into <script> blocks or event handlers, which only execute in the browser
- For redirects decided at render time, return Astro.redirect('/target') from the page instead
Example fix
// before
import { navigate } from 'astro:transitions/client';
navigate('/dashboard'); // executes during SSR -> warning
// after
import { navigate } from 'astro:transitions/client';
if (typeof window !== 'undefined') navigate('/dashboard');
// or, when decided at render time: return Astro.redirect('/dashboard'); Defensive patterns
Strategy: type-guard
Validate before calling
import { navigate } from 'astro:transitions/client';
const inBrowser = typeof window !== 'undefined' && typeof document !== 'undefined';
if (inBrowser) navigate(href); // importing server-side is safe; calling is not Type guard
const inBrowser = typeof window !== 'undefined' && typeof document !== 'undefined';
export function safeNavigate(href: string, opts?: Options) {
if (!inBrowser) return; // SSR no-op instead of a warning
navigate(href, opts);
} Prevention
- Keep navigate() calls inside <script> blocks or DOM event handlers, never frontmatter
- Use Astro.redirect() for redirects decided during rendering
- Route all router calls through one guarded helper in a client-only module
When it happens
Trigger: Invoking navigate() during server rendering: top-level in a shared module, inside .astro frontmatter, or in code paths that execute on both server and client (e.g. a nav utility imported by both).
Common situations: Programmatic navigation based on props or session state (should be a server redirect via Astro.redirect); a shared nav helper running during SSR; copying client router examples into frontmatter scripts.
Related errors
- ResponseSentError
- OnlyResponseCanBeReturned
- Unexpected template-exit instruction without a matching temp
- Unable to render ${displayName} because it is ${Component}!
- Unable to render ${displayName}! This component likely uses
AI-assisted analysis of withastro/astro@52e6c34790 (2026-08-18).
Data as JSON: /api/errors/2a9bc6ec0ea02ac8.
Report an issue: GitHub.