Askr documentation
Fundamentals

Determinism and Runtime Rules

The runtime rules that let the same component tree render identically on the server and in the browser.

Example

Put reactive state inside the component that owns the interaction and derive display values from it during render.

import { derive, state } from '@askrjs/askr';

export function Quantity() {
  const [count, setCount] = state(1);
  const label = derive(() => 'Quantity: ' + count());

  return <button onClick={() => setCount((value) => value + 1)}>{label()}</button>;
}

Render purity

A component function must return the same output for the same props and state on every call — the runtime depends on this to decide what changed and patch only that part of the DOM. Fetching data, subscribing to external events, or mutating shared objects directly inside a component body breaks that guarantee; those belong behind `resource`, `on`, or `task` instead, which run outside the render pass and hand results back through reactive values. Two rules follow directly from re-running the whole function on every update, and the runtime enforces both at runtime, not just by convention: `state()`, `derive()`, lifecycle calls, and `<For>` must run in the same order on every render of a given component — wrapping one of them in a plain `if` (instead of `<Show>`/`<Case>`) throws once the sequence changes — and calling a state setter during render itself (rather than from an event handler or effect) throws `[Askr] state.set() cannot be called during component render.` because it would recurse into another render of the same component.

Stable initial state

`state()` initializers run once, on first render, and are expected to be pure — if the seed value depends on something nondeterministic (like `Math.random()` or `Date.now()`), server-rendered markup and the client's first hydration pass will disagree. Deriving that value from a stable input (a route param, a server-supplied seed) instead keeps the first render identical on both sides.

Browser-only work

Code that touches `window`, `document`, or other browser globals can't run during server rendering, since none of those exist in a Node process. Gate that work behind lifecycle helpers like `task` or `resource`, which only execute after mount on the client, rather than reading browser globals directly in the component body where SSR would throw.

Hydration diagnostics

Server-side rendering shares a `DocumentRenderContext` — including a `seed` value — with the client so both passes render the same output from the same inputs; when they disagree, the runtime surfaces a hydration mismatch. The check itself is a whole-document string comparison (server-rendered markup against a fresh server-side re-render, both normalized), so the diagnostic is a single generic message — `[Askr] Hydration mismatch detected. Server HTML does not match expected server-render output.` — rather than a pointer at which specific subtree diverged. `devWarningsEmitted` on the component instance is a separate mechanism entirely, used for unused-state-variable and slow-render warnings, not hydration.