Askr
Guides

Migration from React

Use migration from react with the current published Askr packages.

  • @askrjs/askr0.0.59

How to use migration from react

Move local values to state, computed values to derive, and effect-owned asynchronous work to cancellable resources before changing routing or rendering mode.

  1. Import the published @askrjs/askr entrypoint.
  2. Keep migration from react configuration next to the component, route, or server composition root that owns it.
  3. Handle pending, unavailable, cancellation, and failure states, then verify the production path.
import { derive, state } from '@askrjs/askr';
import { resource } from '@askrjs/askr/resources';

const projectId = state(initialProjectId);
const project = resource(({ signal }) => loadProject(projectId(), signal), [projectId]);
const title = derive(() => project.data?.name ?? 'Loading project');

Goal and architecture

The core mental shift moving from React is that Askr components are not re-executed on every state change — `state()` returns a `StateTuple` backed by fine-grained reactivity, and reading it inside JSX subscribes that specific expression to updates rather than re-running the whole component function. There's no dependency array to get wrong and no `useMemo`/`useCallback` tax for referential stability, because there's no re-render to guard against in the first place; `derive()` and `selector()` compute values reactively instead.

Implementation

Where React code branches with `{condition && <X/>}` or ternaries, Askr provides `<Show when={...}>`, `<Match>`/`<Case>`, and `<For each={...}>` as components specifically because plain JS conditionals inside JSX don't get the same fine-grained update tracking — using them keeps updates scoped to just the changed piece of the tree. Data fetching maps `useQuery`-style hooks onto `createQuery`/`defineQuery`, which return a query object with `consistency`, `refreshing`, and stale-reason fields instead of a `{ data, isLoading, error }` tuple, and route-level data uses `defer()` plus `<Resolve>` in place of a Suspense boundary.

Failure states

The most common porting mistake is treating a `state()` read like a React value snapshot and destructuring it once at the top of a function — that breaks reactivity, because the fine-grained system needs the read to happen where it's actually used (inside JSX or a `derive()`), not stored in a plain variable ahead of time. Similarly, effects that assumed React's re-render-then-effect ordering may need reworking against Askr's direct reactive update model, since there's no render phase to schedule after.

Verification

After porting a component, verify that updating one piece of state only touches the DOM nodes that actually depend on it — in React that's an optimization you'd check with a profiler, but in Askr's fine-grained model it's the expected baseline behavior and a good smoke test that the port is wired correctly. Re-run existing behavioral tests (form submission, list filtering, route navigation) unchanged where possible, since Askr's `@askrjs/askr/testing` mocks are designed to slot into the same assertion style as React Testing Library patterns.