Routing
Define typed routes, layouts, navigation, loaders, policies, and metadata.
Example
Register a route once and let the same registry drive browser navigation, SSR, and static generation.
import { createRouteRegistry, group, route } from '@askrjs/askr/router';
export const registry = createRouteRegistry(() => {
group({ layout: AppLayout }, () => {
route('/', HomePage);
route('/projects/{projectId}', ProjectPage);
});
});Published props
Generated from the TypeScript declarations shipped by the installed package. Named types in the Type column define the accepted values.
LinkProps
Import from @askrjs/askr/router.
| Prop | Type | Default | Description |
|---|---|---|---|
aria-current | aria-current?: "page" | "step" | "location" | "date" | "time" | "true" | "false" | undefined; | — | Optional aria-current attribute for indicating current page/location. Use "page" for the current page in navigation. |
aria-label | aria-label?: string | undefined; | — | Optional aria-label for accessibility when link text isn't descriptive enough. |
children | children?: RenderableChild; | — | — |
class | class?: string | undefined; | — | — |
href | href?: string | undefined; | — | — |
onClick | onClick?: ((event: MouseEvent) => void) | undefined; | — | — |
onPress | onPress?: ((event: Event) => void) | undefined; | — | — |
rel | rel?: string | undefined; | — | Optional rel attribute for link relationships. Common values: "noopener", "noreferrer", "nofollow" |
target | target?: string | undefined; | — | Optional target attribute. Use "_blank" for new tab/window. |
to | to?: RouteDestination | undefined; | — | — |
ResolveProps
Import from @askrjs/askr/router.
| Prop | Type | Default | Description |
|---|---|---|---|
children | children: (value: T) => RenderableChild; | — | — |
pending | pending?: RenderableChild; | — | — |
rejected | rejected?: RenderableChild | ((error: unknown) => RenderableChild); | — | — |
value | value: Deferred<T>; | — | — |
Route registry
Every route in an Askr app is declared through `createRouteRegistry()`, which takes a definition function and returns a `RouteRegistry` built by calling `route()`, `page()`, `group()`, `index()`, and `fallback()` inside that function. The registry is an ordinary value: export it, then hand it to `createSPA`, `hydrateSPA`, `createStaticGen`, or `renderToString`, so client rendering, hydration, SSG, and SSR all read the same declaration. Because nothing is stored globally, two registries can coexist — which is what lets a static build settle its own route tree without disturbing the one the browser boots from, and what keeps a server rendering concurrent requests off shared mutable state.
Route groups
`group(options, fn)` nests a block of route declarations under a shared layout and shared access rules without repeating them on every child route. The `layout` you pass in `GroupHelperOptions` wraps whatever the group renders, and `auth`/`policies` on the group apply to every route declared inside its callback. Groups compose: nesting one group inside another builds up a `layoutChain` and a merged set of policies that Askr walks top-down when it resolves a request, so a route three groups deep still only needs to declare what's specific to it.
Navigation lifecycle
A navigation in Askr resolves through `resolveRouteRequest()`, which matches the target against the app's explicit `RouteRegistry` and returns either a `RouteRenderResult`, an `AccessRedirectDecision`, an `AccessDenyDecision`, or `null` if nothing matches. That result already reflects any policy checks — redirects and denials are decided before a component ever renders, not caught afterward. Each `RouteContext` handed through the pipeline carries an `AbortSignal`, so loaders and preload functions tied to a route that gets superseded by a newer navigation can be cancelled instead of racing to finish and clobber the current view.
Delivery modes
`RouteMode` is one of `'spa'`, `'ssr'`, or `'ssg'`, and it's threaded through `RouteContext` so a loader or a policy can behave differently depending on how the current request is being served. The same route declarations work across all three modes — you don't write separate route trees for server rendering versus the client. SSG specifically leans on the `entries` option in `RouteOptions`, which returns the list of param combinations to pre-render as static pages at build time.