Core Concepts
Understand components, state, scopes, lifecycle work, and deterministic rendering.
@askrjs/askr0.0.59
How to use core concepts
Put reactive state inside the component that owns the interaction and derive display values from it during render.
- Import the published @askrjs/askr entrypoint.
- Keep core concepts 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 { derive, state } from '@askrjs/askr';
export function Quantity() {
const count = state(1);
const label = derive(() => 'Quantity: ' + count());
return <button onClick={() => count.set(count() + 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 `state(initialValue)`, which returns a tuple-like handle: call it as a function to read the current value, call `.set()` (or destructure the second tuple slot) to write a new one. Reading `count()` 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 with `route()` and served through a manifest, 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 off the same route definitions.
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.