Navigation and Prefetching
How Waku navigates between routes, caches static RSC payloads, and prefetches likely next pages.
How Client Navigation Works
Waku's <Link> component renders an anchor element and handles normal same-tab clicks with the client router. On navigation, Waku fetches the route's RSC payload and updates the current route without a full document reload.
Use <Link> for internal app routes:
import { Link } from 'waku';
export const Nav = () => (
<nav>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
</nav>
);Use a regular <a> element for external URLs, downloads, and links that intentionally open in another browsing context.
Static Route Caching
After a static route has been loaded, Waku can reuse its cached RSC payload on later visits. For example, if the user starts on /, navigates to /about, and then returns to /, the client can render / from the cache instead of requesting that static route again.
Dynamic routes are different. Waku expects dynamic route output to be request-specific, so a visit to a dynamic route may need a fresh server request. Prefetching is most useful when you want to start that work before the user clicks.
Manual Prefetching
Use router.prefetch() when a client component knows which route the user is likely to visit next.
'use client';
import { useRouter } from 'waku';
export const DashboardButton = () => {
const router = useRouter();
return (
<button
onFocus={() => router.prefetch('/dashboard')}
onMouseEnter={() => router.prefetch('/dashboard')}
onClick={() => router.push('/dashboard')}
>
Dashboard
</button>
);
};Link Prefetching
<Link> also has experimental prefetch helpers for common interaction patterns:
import { Link } from 'waku';
export const Nav = () => (
<nav>
<Link to="/docs" unstable_prefetchOnEnter>
Docs
</Link>
<Link to="/blog" unstable_prefetchOnView>
Blog
</Link>
</nav>
);- unstable_prefetchOnEnter starts prefetching when the pointer enters the link.
- unstable_prefetchOnView starts prefetching when the link enters the viewport.
Prefer intent-based prefetching for expensive dynamic routes. View-based prefetching can be useful for short pages with a small number of important links, but it can waste server work if applied to every link in a large list.
Pending UI
<Link> can render adjacent pending UI while a navigation transition is in progress:
import { Link } from 'waku';
const Pending = ({ active }: { active: boolean }) => (
<span aria-hidden style={{ opacity: active ? 1 : 0 }}>
Loading...
</span>
);
export const NavLink = () => (
<Link
to="/reports"
unstable_pending={<Pending active />}
unstable_notPending={<Pending active={false} />}
>
Reports
</Link>
);The pending nodes are rendered next to the link. They do not replace the link's children.
Custom Transitions
For advanced navigation effects, pass unstable_startTransition to control how Waku starts the route transition. One common use case is integrating the browser View Transitions API:
'use client';
import type { ComponentProps } from 'react';
import { Link as WakuLink } from 'waku';
const startViewTransition =
typeof document !== 'undefined' && document.startViewTransition
? (fn: () => void) => {
document.startViewTransition(fn);
}
: undefined;
export const Link = (props: ComponentProps<typeof WakuLink>) => (
<WakuLink {...props} unstable_startTransition={startViewTransition} />
);The prefetch and transition props in this guide are experimental and may change.

