@k8ordo/router

API

Every function, component and type @k8ordo/router exports. Things used together sit side by side, and the types come last.

On this page

defineRoutes

Import from @k8ordo/router

Creates the route table. Each key is a path pattern, and each value is a page component or an object grouping pages.

ts
defineRoutes<R extends RoutesRecord>(record: R): Routes<R>

Parameters

recordRoutesRecord
The table itself. Every key is a pattern starting with /.

Returns

Routes<R> — An object holding the table as passed and match, which matches a path against it.

Caveats

  • The table is checked when this runs, and a mistake in it is a TypeError.
  • Patterns are tried in the order written, and the first that fits wins.
routes.ts
export const routes = defineRoutes({
  '/': Home,
  '/products': {
    layout: ProductsLayout,
    children: { '/': ProductList, '/:id': ProductPage },
  },
  '/*': NotFound,
});

Router

Import from @k8ordo/router

Connects the route table to the browser’s navigation and renders the page that fits the current path. Put it once, at the root of an app that renders in the browser.

ts
<Router routes={Routes} />

Parameters

routesRoutes
The route table defineRoutes returned.

Caveats

  • It takes only navigations to paths the table answers, and leaves the rest to the browser.
  • Opened at a path the table lacks, it renders nothing.
  • It reads the browser’s URL when it mounts, so it cannot render on a server.

Outlet

Import from @k8ordo/router

Marks where, inside a layout, the page it wraps renders.

ts
<Outlet />

Caveats

  • Rendered outside <Router>, it throws. Under the framework, a layout renders children instead.

href

Import from @k8ordo/router

Builds the URL a link points at from a pattern and its params.

ts
href<P extends RegisteredNavigablePattern>(
  pattern: P,
  params?: RegisteredParams<P>,
): string

Parameters

patternP
The pattern to link to. With Register augmented, only the table’s linkable patterns are accepted.
paramsRegisteredParams<P>
The values of the pattern’s params. Left out for a pattern without params.

Returns

string — A URL for an <a>’s href attribute, with the base path in front when the app is served below one.

Caveats

  • Values are encoded with encodeURIComponent.
  • A pattern with /*, a param without a value, or a value with no URL spelling is a TypeError at run time.
ts
href('/products'); // '/products'
href('/products/:id', { id: 42 }); // '/products/42'

Import from @k8ordo/router

Builds a URL from a pattern and its params, and goes there.

ts
navigateTo<P extends RegisteredNavigablePattern>(
  pattern: P,
  params?: RegisteredParams<P>,
  options?: NavigateToOptions,
): NavigationResult

Parameters

patternP
The pattern to link to. With Register augmented, only the table’s linkable patterns are accepted.
paramsRegisteredParams<P>
The values of the pattern’s params. Left out for a pattern without params.
optionsNavigateToOptions
Options for the navigation; the second argument for a pattern without params.

Returns

NavigationResult — The { committed, finished } that the browser’s navigation.navigate() returns. finished resolves once the new page is on screen.

Caveats

  • By default, it pushes a new history entry.
  • Overtaken by another navigation, finished rejects with an AbortError.
ts
await navigateTo('/products/:id', { id: '42' }).finished;
navigateTo('/products', { history: 'replace' });

bindParams

Import from @k8ordo/router

Creates an href and a navigateTo that take some params from a function, for a param every link shares, such as a locale.

ts
bindParams<const Bound extends BoundParams>(
  source: () => Bound,
): BoundLinks<Bound>

Parameters

source() => Bound
A function returning the params to bind, called every time href or navigateTo is.

Returns

BoundLinks<Bound> — An href and a navigateTo that fill in what source returns.

Caveats

  • A bound param may be left out, or passed to override it.
  • When the pattern names a param, the options are always the third argument; with every param bound, pass undefined second.

usePathname

Import from @k8ordo/router

Returns the path the browser is on.

ts
usePathname(): string

Returns

string — The current path, without the base path or a trailing slash.

Caveats

  • It re-renders when the path changes, and never when only the query string does.
  • It changes as soon as the URL does, without waiting for the new page to be on screen.
  • During a server render or hydration, it throws unless it is under <Router> or in a page the framework renders.

useMatch

Import from @k8ordo/router

Checks whether the current path fits a pattern.

ts
useMatch<P extends MatchablePattern>(
  pattern: P,
  options?: MatchOptions,
): ParamsOf<P> | null

Parameters

patternP
A pattern from the table, or one followed by /*, which means “anywhere below it”.
optionsMatchOptions
With inclusive set to true, a /* pattern also fits its own page.

Returns

ParamsOf<P> | null — The params when it fits, or null.

Caveats

  • Built on usePathname, it re-renders only when the path changes.
  • It needs no route table, so it works under the framework.

matchPath

Import from @k8ordo/router

Makes the same check as useMatch, against the path you pass.

ts
matchPath<P extends MatchablePattern>(
  pattern: P,
  pathname: string,
  options?: MatchOptions,
): ParamsOf<P> | null

Parameters

patternP
A pattern from the table, or one followed by /*, which means “anywhere below it”.
pathnamestring
The path to check; a trailing slash is dropped before comparing.
optionsMatchOptions
With inclusive set to true, a /* pattern also fits its own page.

Returns

ParamsOf<P> | null — The params when it fits, or null.

ts
matchPath('/products/:id', '/products/42'); // { id: '42' }
matchPath('/products/*', '/products'); // null
matchPath('/products/*', '/products', { inclusive: true }); // {}

usePendingPathname

Import from @k8ordo/router

Returns the path of the page being loaded.

ts
usePendingPathname(): string | null

Returns

string | null — The path of the page being loaded, or null when none is.

Caveats

  • It is set as a navigation starts, and goes back to null once the new page is on screen, or when the navigation is abandoned or its load fails.
  • An update that changes only the query string sets nothing.
  • It is null in a server render.

useParams

Import from @k8ordo/router

Reads a page’s params, typed by its pattern.

ts
useParams<P extends RegisteredPattern>(pattern: P): ParamsOf<P>

Parameters

patternP
The pattern of the page this component renders as.

Returns

ParamsOf<P> — The params, typed by the pattern. Every value is a string.

Caveats

  • Rendered under another pattern, it throws.
  • It works only under <Router>, not under the framework.

useRoute

Import from @k8ordo/router

Returns the pattern that won and its params, untyped.

ts
useRoute(): {
  pattern: string;
  params: Readonly<Record<string, string>>;
}

Returns

Pick<Match, 'pattern' | 'params'> — The winning pattern and its params.

Caveats

  • It throws outside <Router>, or where no page matched.

normalizePathname

Import from @k8ordo/router

Tidies a path by the router’s own rule: trailing slashes are dropped, and / alone is kept.

ts
normalizePathname(pathname: string): string

Parameters

pathnamestring
The path to tidy.

Caveats

  • Only trailing slashes go: repeated slashes inside the path and percent-encoding are left alone.
ts
normalizePathname('/products/'); // '/products'
normalizePathname('/'); // '/'

withBase

Import from @k8ordo/router

Puts the base path the app is served under in front of a path in the table’s terms.

ts
withBase(pathname: string, base?: string): string

Parameters

pathnamestring
A path in the table’s terms.
basestring
The base path. Left out, it is read from import.meta.env.BASE_URL; code Vite does not process passes it.

Returns

string — The path with the base path in front.

Caveats

  • A relative base such as ./ adds and removes nothing.

withoutBase

Import from @k8ordo/router

Takes the base path the app is served under off a URL’s path.

ts
withoutBase(pathname: string, base?: string): string | null

Parameters

pathnamestring
A URL’s path.
basestring
The base path. Left out, it is read from import.meta.env.BASE_URL; code Vite does not process passes it.

Returns

string | null — The path without the base path, or null for a path outside it.

Caveats

  • A relative base such as ./ adds and removes nothing.
ts
withBase('/products', '/docs/'); // '/docs/products'
withoutBase('/docs/products', '/docs/'); // '/products'
withoutBase('/elsewhere', '/docs/'); // null

notFound

Import from @k8ordo/router

Says, from a framework page, that its path has no page after all.

ts
notFound(): never

Caveats

  • It throws, so nothing after it runs.
  • Under @k8ordo/static and @k8ordo/server, the nearest not-found.tsx answers under a 404.
  • Under <Router>, it is an error like any other.

isNotFound

Import from @k8ordo/router

Checks whether a thrown value is what notFound() throws.

ts
isNotFound(value: unknown): boolean

Parameters

valueunknown
The value to check.

Caveats

  • It looks for a Symbol.for brand rather than a class, so it works even with two copies of this package loaded.

PathnameProvider

Import from @k8ordo/router

Gives usePathname its path during a server render and hydration.

ts
<PathnameProvider pathname={string}>{children}</PathnameProvider>

Parameters

pathnamestring
The path this render is for.

Caveats

  • <Router> and the framework’s runtime provide it themselves. An app writes it only when it builds its own host on useInterceptedNavigation.

Import from @k8ordo/router

A context that tells the route table’s error a new page is on screen.

ts
<NavigationGeneration value={number}>
  {children}
</NavigationGeneration>

Parameters

valuenumber
The generation useInterceptedNavigation returns.

Caveats

  • <Router> and the framework’s runtime provide it themselves.

useInterceptedNavigation

Import from @k8ordo/router

The navigation half of <Router>, on its own: it loads and applies the navigations it takes, and finishes each once the new page is on screen.

ts
useInterceptedNavigation<T>(
  handler: NavigationHandler<T>,
): { readonly generation: number }

Parameters

handlerNavigationHandler<T>
The functions that decide whether to take a navigation, what to load, and how to apply it.

Returns

{ readonly generation: number } — generation, which changes each time a new page is on screen.

Caveats

  • Render what apply set through useDeferredValue, in the same component.
  • handler is read at event time, so a new object on every render is fine.

Routes

Import from @k8ordo/router

The type of the route table defineRoutes returns.

ts
type Routes<R extends RoutesRecord = RoutesRecord> = {
  kind: 'routes';
  record: R;
  match: (
    pathname: string,
    accept?: (match: Match) => boolean,
  ) => Match | null;
};

Fields

kind'routes'
Always routes.
recordR
The table passed to defineRoutes.
match(pathname, accept?) => Match | null
Matches a path, returning the Match or null. When accept returns false, that result is passed over and the walk goes on to the next pattern.

RoutesRecord

Import from @k8ordo/router

The type of a table: keys are patterns starting with /, values are RouteNodes.

ts
type RoutesRecord = Record<`/${string}`, RouteNode>;

RouteNode

Import from @k8ordo/router

The type of a value in the table: a page component, or an object with children.

ts
type RouteNode =
  | RouteComponent
  | {
      layout?: RouteComponent;
      error?: ErrorComponent;
      loading?: ComponentType;
      children: RoutesRecord;
    };

Fields

layoutRouteComponent
The layout wrapping the pages below.
errorErrorComponent
What renders in place of a page below that throws.
loadingComponentType
What shows while a page below suspends. It receives no props.
childrenRoutesRecord
The table of pages below; its keys continue the parent’s pattern.

RouteComponent

Import from @k8ordo/router

The type of a page or layout: a component declaring any props.

ts
type RouteComponent = ComponentType<never>;

Caveats

  • <Router> passes no props and the framework passes params and more, so it is ComponentType<never> to let either render the same table.

Match

Import from @k8ordo/router

The type of what match returns.

ts
type Match = {
  pattern: string;
  params: Readonly<Record<string, string>>;
  stack: readonly RouteComponent[];
};

Fields

patternstring
The winning pattern, spelled as in the table.
paramsReadonly<Record<string, string>>
The decoded param values.
stackreadonly RouteComponent[]
The components to render, outermost layout first and the page last.

ErrorProps

Import from @k8ordo/router

The props the route table’s error component receives.

ts
type ErrorProps = {
  readonly error: unknown;
  readonly reset: () => void;
};

Fields

errorunknown
What was thrown.
reset() => void
Renders the page below again, in place.

ErrorComponent

Import from @k8ordo/router

The type of the component the route table’s error names.

ts
type ErrorComponent = ComponentType<ErrorProps>;

Import from @k8ordo/router

The type of navigateTo’s options.

ts
type NavigateToOptions = {
  history?: 'push' | 'replace';
};

Fields

history'push' | 'replace'
'push' adds a new entry and 'replace' replaces the current one. The default is 'push'.

MatchOptions

Import from @k8ordo/router

The type of useMatch’s and matchPath’s options.

ts
type MatchOptions = {
  readonly inclusive?: boolean;
};

Fields

inclusiveboolean
Lets a /* pattern fit its own page too. It changes nothing for a pattern not ending in /*.

MatchablePattern

Import from @k8ordo/router

The patterns useMatch and matchPath take: one from the table, or a linkable pattern followed by /*.

ts
type MatchablePattern =
  | RegisteredPattern
  | `${RegisteredNavigablePattern}/*`;

BoundParams

Import from @k8ordo/router

What the function given to bindParams returns: values with one URL spelling, by param name.

ts
type BoundParams = Readonly<Record<string, ParamValue>>;

Import from @k8ordo/router

The type of what bindParams returns.

ts
type BoundLinks<Bound extends BoundParams> = {
  readonly href: (pattern, params?) => string;
  readonly navigateTo: (
    pattern,
    params?,
    options?: NavigateToOptions,
  ) => NavigationResult;
};

Fields

href(pattern, params?) => string
An href that fills in the bound params.
navigateTo(pattern, params?, options?) => NavigationResult
A navigateTo that fills in the bound params.

Import from @k8ordo/router

The type of the functions useInterceptedNavigation takes.

ts
type NavigationHandler<T> = {
  claim: (url: URL) => boolean;
  load: (url: URL, signal: AbortSignal) => T | Promise<T>;
  apply: (value: T) => void;
  refresh?: (url: URL) => boolean;
};

Fields

claim(url: URL) => boolean
Whether to take this navigation, answered synchronously during the event.
load(url: URL, signal: AbortSignal) => T | Promise<T>
Produces what to render for the URL. signal aborts when the navigation is overtaken.
apply(value: T) => void
Applies it, as an ordinary update outside any transition.
refresh(url: URL) => boolean
Whether a navigation that keeps the path should still load. Left out, it never does.

Register

Import from @k8ordo/router

The interface a route table is registered on. Add routes with declare module, and patterns are checked against the table.

ts
declare module '@k8ordo/router' {
  interface Register {
    routes: typeof routes;
  }
}

Caveats

  • Register once, in the app.
  • Under @k8ordo/static and @k8ordo/server it is generated into .k8ordo/register.gen.ts, with params and search, and under @k8ordo/server request too.

RegisteredPattern

Import from @k8ordo/router

The pattern of every page in the registered table, /* patterns included. Before registering, any string starting with /.

ts
type RegisteredPattern = Register extends {
  routes: Routes<infer R>;
}
  ? PatternOf<R>
  : `/${string}`;

RegisteredNavigablePattern

Import from @k8ordo/router

The registered table’s patterns a link can point at, which leaves out those with /*. Before registering, any string starting with /.

ts
type RegisteredNavigablePattern = Register extends {
  routes: Routes<infer R>;
}
  ? NavigablePatternOf<R>
  : `/${string}`;

RegisteredParams

Import from @k8ordo/router

The params a link to pattern P takes. A param a schema typed takes that type; the others take a ParamValue or a string.

ts
type RegisteredParams<P extends string>

RegisteredPageParams

Import from @k8ordo/router

The params the page at pattern P receives: a schema’s output where one ran, and strings elsewhere.

ts
type RegisteredPageParams<P extends string>

PageProps

Import from @k8ordo/router

The props a framework page receives, by the pattern its directory stands for.

ts
type PageProps<P extends RegisteredPattern> = {
  readonly params: RegisteredPageParams<P>;
  readonly pathname: string;
} & RequestProps &
  SearchProps<P>;

Fields

paramsRegisteredPageParams<P>
The params, typed by the schemas.
pathnamestring
The path this render is for.
requestRequest
The request; only under @k8ordo/server.
searchunknown
What a page that exports search reads; only on such a page.

LayoutProps

Import from @k8ordo/router

The props a framework layout receives. Its type argument must be a pattern with a page.

ts
type LayoutProps<P extends RegisteredPattern> = {
  readonly params: ParamsOf<P>;
  readonly pathname: string;
  readonly children: ReactNode;
} & RequestProps;

Fields

paramsParamsOf<P>
The params, typed as strings even with a schema.
pathnamestring
The path this render is for.
childrenReactNode
The page the layout wraps.
requestRequest
The request; only under @k8ordo/server.

RouteContext

Import from @k8ordo/router

The type of what a framework route.ts handler receives.

ts
type RouteContext<P extends RegisteredPattern> = {
  readonly request: Request;
  readonly params: RegisteredPageParams<P>;
};

Fields

requestRequest
The incoming request.
paramsRegisteredPageParams<P>
The params, typed by the schemas as a page’s are.

PatternOf

Import from @k8ordo/router

Every page pattern in table R, as written, /* patterns included.

ts
type PatternOf<R extends RoutesRecord>

Import from @k8ordo/router

The patterns of table R a link can point at, which leaves out those with /*.

ts
type NavigablePatternOf<R extends RoutesRecord>

Import from @k8ordo/router

Path when it fits one of the table’s linkable patterns segment by segment, and never when none fits.

ts
type NavigablePath<D, Path extends string>

Caveats

  • Where a pattern has a :param, any one non-empty segment fits.
  • A path with a trailing slash is never.
ts
type A = NavigablePath<typeof routes, '/products/42'>;
// '/products/42'
type B = NavigablePath<typeof routes, '/products/42/reviews'>;
// never

ParamsOf

Import from @k8ordo/router

The params derived from a pattern string, every value a string.

ts
type ParamsOf<Pattern extends string>
ts
type Params = ParamsOf<'/:locale/products/:id'>;
// { locale: string; id: string }

PathFor

Import from @k8ordo/router

The type of a path that fits the pattern, with any string where each :param is.

ts
type PathFor<Pattern extends string>
ts
type Path = PathFor<'/:locale/products/:id'>;
// `/${string}/products/${string}`

ParamValue

Import from @k8ordo/router

The values with exactly one URL spelling, which a link’s params take.

ts
type ParamValue = string | number | bigint | boolean;

StandardSchemaLike

Import from @k8ordo/router

The Standard Schema shape, which a schema from any library implementing it fits, zod and zod/mini included.

ts
type StandardSchemaLike<Output = unknown> = {
  readonly '~standard': {
    readonly types?: { readonly output: Output } | undefined;
  };
};

SchemaOutput

Import from @k8ordo/router

The type of what a schema produces.

ts
type SchemaOutput<Schema> =
  Schema extends StandardSchemaLike<infer Output> ? Output : never;

ParamsSchemaFor

Import from @k8ordo/router

A schema that can be pattern P’s paramsSchema: its output’s keys are some of the pattern’s params.

ts
type ParamsSchemaFor<Pattern extends string> = StandardSchemaLike<
  Partial<Record<keyof ParamsOf<Pattern> & string, unknown>>
>;

ParsedParams

Import from @k8ordo/router

A pattern’s params after a list of schemas has run, outer layouts first and the page last. A param no schema covers stays a string.

ts
type ParsedParams<
  Pattern extends string,
  Schemas extends readonly unknown[],
>

ParsedParamsMap

Import from @k8ordo/router

Turns each pattern’s list of schemas into its ParsedParams; the generated Register’s params is one.

ts
type ParsedParamsMap<
  Schemas extends Record<string, readonly unknown[]>,
>

Caveats

  • The schema types are mostly for the generated code; what an app writes by hand is PageProps and LayoutProps.
k8ordo

React libraries that use Baseline features without holding back.

© 2026 k8o — MIT License

Typeset in Noto Sans JP & M PLUS 2