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> 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.

designed bycandycode alternative graphic design web development agency San Diego