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

  1. Guard the call: if (typeof window !== 'undefined') navigate('/target')
  2. Move navigation into <script> blocks or event handlers, which only execute in the browser
  3. 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

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


AI-assisted analysis of withastro/astro@52e6c34790 (2026-08-18). Data as JSON: /api/errors/2a9bc6ec0ea02ac8. Report an issue: GitHub.