Behavioral Contracts
Use behavioral contracts with the current published Askr packages.
@askrjs/askr0.0.59
How to use behavioral contracts
Preserve ownership, cancellation, deterministic rendering, and delivery parity when composing lower-level APIs into application features.
- Import the published @askrjs/askr entrypoint.
- Keep behavioral contracts 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 { resource } from '@askrjs/askr/resources';
const project = resource(
({ signal }) => api.projects.get(projectId(), { signal }),
[projectId]
);
// The component owns this request; disposal aborts it.Render contract
A component function is a `ComponentFunction`: it takes `Props` and an optional `ComponentContext` (carrying a `signal` and, during SSR, an `SSRContext`) and returns a `JSXElement` or `VNode`. Calling `state()` is only valid during that render call — it reads the active component instance from context, and calling it outside a component function throws, per the runtime's own guard.
Ownership
Reactive scope is threaded with `defineScope()`/`readScope()` rather than any global mutable state — `defineScope(defaultValue)` creates a `Scope<T>` you provide via a wrapping element, and `readScope()` reads the nearest value down the component tree. Internally each mounted component tracks its own `ownerFrame`, `stateValues`, and cleanup functions, so state and scope values are torn down with the instance that created them.
Cancellation
Every component receives an `AbortSignal` through its render context, and `ComponentInstance` holds the backing `AbortController` used to cancel in-flight work when the component unmounts. `resource()` (from @askrjs/askr/resources) is built on this — it passes the signal into your async function so a `fetch` or other cancelable call stops when the component goes away, instead of resolving into a dead component.
Delivery parity
The same component tree renders through server rendering (SSR), static generation (SSG), and client hydration via a shared `DocumentRenderContext` (mode, url, params, seed, route) — the framework doesn't fork behavior between server and client render paths. Hydration reconciles the server-rendered markup against the client's first render pass rather than re-rendering from scratch, so component output needs to be stable across both invocations for hydration to succeed cleanly.