@k8ordo/router

Bind a param every link shares

Some segments sit at the front of nearly every path in an app, such as a locale or a tenant. Passing that value to every link is tedious, so gather it in one place with bindParams.

On this page

Hand over a function that returns the value

bindParams takes a function that returns the params to bind. The href and navigateTo it gives back call that function every time they are called, and fill in the params it supplies.

src/links.ts
import { bindParams } from '@k8ordo/router';

import { locales } from './i18n';

export const { href, navigateTo } = bindParams(() => ({
  locale: locales.getLocale(),
}));

This site’s own src/links.ts is written exactly this way. locales.getLocale() comes from @k8ordo/i18n and returns the current locale.

The app’s code then imports href and navigateTo from this module rather than from @k8ordo/router.

Use the bound links

Patterns keep their full /:locale/… spelling. The bound locale may be left out, so only the other params are passed. The results in the comments are for a current locale of ja.

ts
import { href, navigateTo } from './links';

href('/:locale/products/:id', { id: '42' }); // '/ja/products/42'
href('/:locale', { locale: 'en' }); // '/en'
navigateTo('/:locale/products');

A bound param can still be passed to override it, for a link to another locale’s page, as a language switcher needs.

Since the patterns are spelled as before, the Register check still applies: a pattern the table lacks, or a missing param that is not bound, is a type error as usual.

The function is read on every call

The binding function does not run once, when bindParams is called. It runs every time href or navigateTo does.

A value that differs per request or per URL is therefore read where the link is built, and calling bindParams once at the top level of a module is enough.

Which package supplies the value is the app’s business. The router knows a param’s name and nothing more; it has no idea what a locale is.

Options go third

Even for a pattern whose params are all bound, navigateTo’s options are the third argument. Pass undefined second, in place of the params.

ts
navigateTo('/:locale', undefined, { history: 'replace' });
navigateTo('/:locale', { locale: 'en' }, { history: 'replace' });
navigateTo('/:locale', { history: 'replace' });
Options in second place: a type error

Params and options are both plain objects, so whether the pattern names a param is what decides which the second argument is. When it does, the second argument is always the params.

Warning

Pitfall

Passed second around the type check, the options are read as params: history is ignored, and a new history entry is pushed.

k8ordo

React libraries that use Baseline features without holding back.

© 2026 k8o — MIT License

Typeset in Noto Sans JP & M PLUS 2