Definitions and Layouts
Declare routes, nested layouts, and groups in one typed registry you can read and enumerate.
Example
const registry = createRouteRegistry(() => {
group({ layout: AppLayout }, () => {
route('/', HomePage);
route('/projects/{projectId}', ProjectPage);
});
});Route definitions
route(path, Component, options?) is the building block for a single URL: it returns a typed RouteRef you can later pass to to() for building destinations, and it accepts a RouteOptions object for loaders, policies, search-param schemas, and metadata. page(path, Component, fn) is the nested-route sibling — it declares a route at path and opens a child definition scope, so pages under it can add their own sub-routes or a layout. Both infer route params from the path string itself: writing route('/posts/{slug}', ...) gives the component a typed slug: string prop with no manual generic annotation needed.
Groups and layouts
A layout is just a component with a children prop, attached to a group via GroupHelperOptions.layout or to a page via the layout argument on page(). Askr stores it as a LayoutScopeRecord and composes it automatically at render time — you never call the layout component yourself. Layout composition happens outside the page chain: pages inside a group render first through Outlet, and the layout wraps that result, so a loading state or error inside a page doesn't require the layout to know anything about it.
Nested pages
page() lets a route own child routes: the fn argument is a definition callback where you call route() or nest further page() calls scoped under that parent path. Each nested route contributes to the same RouteRecord.segments matching logic, and Askr ranks routes by specificity so a literal segment like /posts/new wins over a param segment like /posts/{id} when both could match. index(Component, options?) fills the special case of "what renders at this path with nothing appended" — the default child when a parent route is visited directly.
Fallback routes
fallback(Component) registers the component Askr renders when no other route matches, and it's marked internally with isFallback: true on its RouteRecord so the router treats it distinctly from an ordinary catch-all. You get one fallback per scope, not one per RouteRegistry overall — a root-level fallback() is your app's 404 page, and each page() can additionally declare its own fallback() scoped to that page's own path prefix (nesting one inside a group() instead of directly in a page scope throws). It's meant for 404-style pages, not a general-purpose wildcard route — if you need a wildcard that still participates in normal matching and layout composition, use a * or {*splat} path segment on a regular route() instead.