Skip to content

createRouter()

Signature

function createRouter<T extends RouteDefinition[]>(
routes: T,
options?: RouterOptions,
): Router<T>;

Parameters

routes — required

An array of route definitions. Each route has shape:

type RouteDefinition = {
path: string;
handler: () => Promise<unknown>;
meta?: Record<string, unknown>;
};
FieldTypeDescription
pathstringURL pattern. Supports literals (/about), params (:id), and a trailing wildcard (*).
handler() => Promise<unknown>Called on match. Typically () => import('./page'). The resolved value is exposed on match.handler().
metaRecord<string, unknown>Optional. Arbitrary data attached to the route — useful for requiresAuth, layout, page metadata, etc. Available as match.meta.

options — optional

type RouterOptions = {
base?: string;
hash?: boolean;
scrollBehavior?: 'auto' | 'instant' | 'smooth' | 'manual';
};
OptionDefaultDescription
base'/'Base path for all routes. Set to '/app/' if your SPA lives at example.com/app/.
hashfalseUse #-based URLs instead of HTML5 history. Useful when deploying to environments that can’t be configured to fall back to index.html.
scrollBehavior'auto'What to do on navigation. 'manual' disables routekit scroll handling entirely.

Returns

A Router<T> instance:

type Router<T> = {
start: () => void;
go: (target: string | NavigateTarget) => void;
replace: (target: string | NavigateTarget) => void;
back: () => void;
forward: () => void;
match: (url: string) => Match | null;
on: <E extends RouterEvent>(event: E, handler: RouterEventHandler<E>) => () => void;
url: string; // current URL (read-only)
routes: T; // the route table you passed in
};

Examples

Minimal

import { createRouter } from 'routekit';
const router = createRouter([
{ path: '/', handler: () => import('./pages/home') },
{ path: '/posts/:id', handler: () => import('./pages/post') },
]);
router.start();

With base path

If your app is served at https://example.com/docs/:

const router = createRouter(routes, { base: '/docs/' });

This is what PUBLIC_BASE_PATH in this template’s astro.config.mjs configures for Starlight — the same idea.

With route metadata

const routes = [
{
path: '/admin',
handler: () => import('./pages/admin'),
meta: { requiresAuth: true, layout: 'admin' },
},
] as const;
const router = createRouter(routes);
router.on('navigate', ({ match }) => {
if (match.meta?.requiresAuth && !isLoggedIn()) {
router.replace('/login');
return;
}
// ... render
});

Caveats

  • Order matters. The first matching route wins. Put specific routes before generic ones. See match order.
  • as const for type safety. Without it, routes[N].path widens to string and you lose param inference. See TypeScript usage.
  • Single router per app. Creating multiple createRouter() calls is supported but rarely useful — they’ll all attach delegated <a> interceptors and may double-handle clicks. If you need conditional routing, use route meta and branch in your 'navigate' handler.