Skip to content
wayfinder logoWayfinderRouting
Framework-agnostic client-side router with typed params, async data loading, middleware, leave guards, and View Transitions support.
Version
v2.0.0
Size
6.1 KB gzip
Dependencies
Zero dependencies
BrowserNode ≥22SSRDeno
createRoutercreateBrowserHistorycreateMemoryHistoryredirectToWayfinderError View all 10 exports

Why Wayfinder?

Managing navigation by hand means scattered popstate listeners, duplicated path checks, and no shared abstraction for loading data or blocking navigation. Wayfinder moves all of that into one declarative table.

ts
// Before — manual navigation with popstate
window.addEventListener('popstate', () => {
  const path = window.location.pathname;
  if (path === '/') renderHome();
  else if (path.startsWith('/dashboard')) renderDashboard();
  else renderNotFound();
});
document.querySelectorAll('a[data-route]').forEach((a) => {
  a.addEventListener('click', (e) => {
    e.preventDefault();
    history.pushState({}, '', (e.currentTarget as HTMLAnchorElement).href);
    dispatchEvent(new PopStateEvent('popstate'));
  });
});

// After — with Wayfinder
import { createRouter } from '@vielzeug/wayfinder';

const router = createRouter({
  routes: {
    home: { path: '/' },
    dashboard: { path: '/dashboard' },
  },
  notFound: { component: NotFoundPage },
});

router.subscribe((state) => {
  render(state.matches.at(-1)?.component);
});

Use Wayfinder when you need named navigation, route-level data loading with cancellation, middleware, or leave guards in a framework-agnostic setup.

Consider a framework's built-in router when you are deep in a single framework ecosystem (React Router, Vue Router) and want first-class component binding with no adapter layer.

FeatureWayfinderpage.jsNavigo
Bundle size6.1 KB~1 kB~5 kB
History mode
Memory history (tests / non-browser)
Typed path params
Named navigationPartial
Middleware
Data loaders with AbortSignal
Lazy route loading
Declarative redirects
Search param validation
Error in state
History state in context
Leave guards
Hover prefetching (preload())
Scroll restoration
View Transition API
Zero dependencies

Installation

sh
pnpm add @vielzeug/wayfinder
sh
npm install @vielzeug/wayfinder
sh
yarn add @vielzeug/wayfinder

Quick Start

Create a memory-backed router, wait for initial routing, then navigate by name.

ts
import { createMemoryHistory, createRouter } from '@vielzeug/wayfinder';

const router = createRouter({
  history: createMemoryHistory('/'),
  routes: {
    home: { path: '/' },
    settings: { path: '/settings' },
  },
});

await router.ready;
await router.navigate({ name: 'settings' });
console.log(router.getSnapshot().location.pathname); // /settings
router.dispose();

Features

  • createRouter() — Compiles named, nested route tables.
  • navigate() — Commits route changes after middleware reaches its terminal stage.
  • ready — Signals that initial routing has settled.
  • data() — Receives cancellation through AbortSignal and can stream async-generator updates.
  • beforeLeave() — Blocks route exits before history changes.
  • matchPath() / loadPath() — Inspect routes synchronously or load route data without navigation.
  • preload() — Warms route data for a later matching navigation.
  • createMemoryHistory() — Runs routers in tests and non-browser environments.
  • debugRouter() — Logs navigation state from @vielzeug/wayfinder/devtools.

Documentation

See Also

  • Ripple — reactive signals; sync router state to a signal for framework-agnostic reactivity
  • Ward — permission guards; use inside Wayfinder middleware to protect routes
  • Herald — event bus; dispatch route-change events to decouple navigation side effects