The table lives in one place. Pages never import it — a typed link needs only the pattern string.
:param segments, wildcards and groups that structure without appearing in the URL, all in one table. Matching is in declaration order, first match wins — no specificity ranking to reason backwards from.Register is declared a pattern the table does not have fails to compile. No code generation.<a> is already a client navigation. Wrapping it would add a second way to write the same thing; href is what makes it typed.useMatch('/products/*') answers whether a page under a section is showing. It needs no table in the browser, which is why a sidebar under the framework asks with it too.error beside a layout and, when what is below throws, it renders inside the layout with the frame intact. Scrolling a new page to the top — or to its #fragment — is the router’s job too.The guide ships inside the npm package. An AI coding assistant reads the exact installed version out of node_modules/@k8ordo/router/docs/.
// routes.ts
export const routes = defineRoutes({
'/': Home,
'/products': {
children: { '/': ProductList, '/:id': ProductPage },
},
'/(docs)': { layout: DocsLayout, error: DocsError, children: { '/guide': Guide } },
'/*': NotFound,
});
// どのページからでも。表は import しない
<a href={href('/products/:id', { id })}>…</a>;
const { id } = useParams('/products/:id');
const inProducts = useMatch('/products/*') !== null; // 表を持たないブラウザでも