These docs cover 0.2.0-beta.26. Each guide distinguishes supported beta behavior, explicit deployment boundaries, and retained 0.1 compatibility material.

Browse documentationAll guides

Build · browser behavior

State, actions, and effects

Presolve uses ordinary property access backed by compiler-owned dependency and update plans. There are no signal wrappers, .value reads, runtime proxies, or authored dependency arrays.

Declare owned state

Initialize a component-owned field with state(initialValue). Read it as an ordinary field in TSX, getters, actions, and admitted effects.

import { action, Component, state } from "presolve";export class Counter extends Component {count = state(0);increment = action(() => {this.count += 1;});render() {return <button onClick={this.increment}>Count: {this.count}</button>;}}

Write through actions

action(handler) creates an instance-field action with the component receiver preserved. The beta admits synchronous state operations and compiler-proven calls. Pass the action directly to a browser event whenever no argument is needed.

Reads are immediateA later statement in the same action observes an earlier state write.
Flushes are boundedDerived values, effects, and DOM patches run after the outer action completes.
Writes are ownedReactive state cannot be mutated from arbitrary methods or effects.

Parameters and local values

Typed primitive parameters and serializable local literals are admitted in the closed beta action surface. A static event closure can capture an exact value.

select = action((id: number) => {const next = id;this.selectedId = next;});return <button onClick={() => this.select(42)}>Select</button>;
Synchronous beta boundary

Async handlers, unproven captures, arbitrary object mutation, and unsupported statements fail closed. Presolve does not execute the handler through a generic reactive runtime.

Derive with a pure getter

A synchronous getter becomes a computed candidate when the compiler proves it pure. Dependencies, caching, and invalidation are derived automatically.

get remaining(): number {return this.todos.filter((todo) => !todo.done).length;}

Do not call computed() in new 0.2 source. Computed getters cannot write state, invoke actions, or perform capability work.

Run a browser effect and clean it up

effect(handler) declares synchronous terminal browser work. The compiler schedules it after initial render and after an affected action batch. Returning a cleanup function ties disposal to the exact component occurrence.

syncTitle = effect(() => {document.title = `${this.remaining} remaining`;return () => {document.title = "Presolve app";};});

Effects cannot mutate Presolve state or call actions/effects. On branch replacement, keyed removal, or parent teardown, cleanup runs child-first before the occurrence is released.

Execution order

  1. Action beginsParameters and serializable locals are assigned.
  2. State writes applyReads later in the same outer action see them.
  3. Computed values invalidateOnly compiler-derived dependents participate.
  4. DOM plans executeBindings and structural hosts receive exact patches.
  5. Effects runAffected effects execute after the completed update batch.