Loaders and Deferred Values
Use loaders and deferred values with the current published Askr packages.
@askrjs/askr0.0.59
How to use loaders and deferred values
Return critical loader data directly and wrap slower independent work in defer so the page can stream a stable shell.
- Import the published @askrjs/askr entrypoint.
- Keep loaders and deferred values configuration next to the component, route, or server composition root that owns it.
- Handle pending, unavailable, cancellation, and failure states, then verify the production path.
import { defer } from '@askrjs/askr/router';
route('/projects/:projectId', ProjectPage, {
loader: async ({ params }) => ({
project: await projects.get(params.projectId),
activity: defer(activity.list(params.projectId)),
}),
});Route loaders
`RouteOptions.loader` is a function that receives the route's `RouteContext` plus an optional `request`, and its return value becomes the data available to the route on first render — including during SSR, where it runs before the response streams. `preload` is a separate hook meant for warming query caches ahead of render rather than blocking on the result, and it's handed a `QueryPrefetchContext` alongside the usual context fields. Both run per-navigation, keyed to the route that's about to become active, not on some global schedule.
Deferred values
`defer(promise)` wraps a `PromiseLike<T>` into a `Deferred<T>` you can return from a loader without blocking the initial render on it — the page renders immediately and the deferred value resolves in the background. `isDeferred()` checks whether a given value is one of these wrappers, which loaders and data-fetching code use to decide whether to await it directly or hand it to `Resolve`. `routeData<T>()` reads whatever the current route's loader returned, deferred values included, from inside the rendering component.
Pending and rejected states
A `Deferred<T>` exposes its own `state` (`'pending' | 'fulfilled' | 'rejected'`), plus `value` and `error` fields that update as the underlying promise settles. The `Resolve` component is the declarative way to render against that state: give it a `value`, a `pending` fallback, an optional `rejected` fallback (a node or a function of the error), and a `children` render function that only runs once the value is fulfilled. That keeps loading and error UI colocated with the data they describe instead of scattered across manual `if` checks.
Cancellation
Every loader and `preload` call gets a `signal: AbortSignal` on its context, and Askr aborts it automatically if a newer navigation supersedes the one that triggered it — so a slow loader for a route the user already navigated away from doesn't finish late and overwrite current state. `resolveDeferredValues(input, signal)` walks a data structure and resolves any nested `Deferred` values it finds, respecting that same signal so a whole tree of pending data can be cancelled together rather than one promise at a time.