Core Concepts
Understand components, state, scopes, lifecycle work, and deterministic rendering.
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>;
}Components are functions
An Askr component is a plain function that runs during render and returns JSX. There's no class, no `this`, and no separate render method to override — `function Counter() { const [count, setCount] = state(0); return <button onClick={() => setCount(v => v + 1)}>{count()}</button>; }` is a complete component. Because a component is just a function, testing one is calling it with props and checking what comes back.
State is explicit
State cells are created with `const [value, setValue] = state(initialValue)`. Call the getter to read and the setter to write. Reading `value()` inside a component subscribes that render to future changes, so nothing re-renders unless it actually read a stale signal. `state()` must run during a component's render call — it captures the owning component instance from context, so calling it at module scope throws.
Routes compose the application
An Askr app isn't one big tree rendered from a single root — it's a set of routes registered through an explicit `RouteRegistry`, each one mapping a path to the component that owns it. Layouts and groups nest naturally because a route's component can render further routed children, so shared chrome only has to be written once. This keeps navigation, code-splitting, and server rendering all working from the same registry.
Rendering stays deterministic
Given the same props and state, a component must produce the same output every time it's called — the runtime relies on this to diff and patch the DOM correctly and to make server-rendered markup match what the client hydrates. Side effects like fetches, timers, or DOM reads belong in lifecycle helpers (`resource`, `task`, `on`), not directly in the function body. Breaking purity shows up as hydration mismatches or inconsistent re-renders, which is why the runtime treats it as a hard rule rather than a suggestion.