Skip to content

Which primitive should I use? ​

There are five primitives. They share the same shape — a name, a configuration, optional insertions — and differ only in where the value comes from and what triggers it.

The decision ​

Where does the value live?Use
In memory, you own itstate
On a server, readquery
On a server, writtenmutation
In the URL's query stringqueryParams
Nowhere — it's an action with a lifecycleasyncProcess

The same table, by symptom ​

"I need a value the user can change." → state. It is the default. Reach for anything else only when the value's home is somewhere other than memory.

"I need to display data from an API." → query. It re-runs when its params change and carries isLoading / status / exception for you. Don't put a query result into a state — that's two sources of truth.

"I need to send something to an API." → mutation. Triggered explicitly with .mutate(...). Connect it back to the read side with insertReactOnMutation rather than reloading by hand.

"This filter should survive a refresh and be shareable." → queryParams. The URL becomes the source of truth; your query's params read from it.

"I need to run an async thing and know if it's running." → asyncProcess. Use it for operations that are not a server read or write: a file export, a share sheet, a delay, a Web API call.

Things that are not a primitive ​

  • Derived values — use craftComputed inside an insertion. Craft keeps the reader dependency visible in the graph.
  • Reusable logic across primitives — that's an insertion, not a primitive.
  • A group of primitives with a name and a scope — that's a craftService.

What they have in common ​

Whichever you pick, the mechanics are identical: the name comes first, the result is the primitive reference itself, yield* drives it inside any craft generator, and the last argument is an insertion.

That shared shape is one page: Anatomy of a primitive. Read it once and every primitive page becomes just its own specifics. Advanced patterns that need to write a primitive from DI — wrappers, registries, WebMCP tools, seeding a query result — use the injectable runtime context.

See Also ​