Guides

Performance and bundle boundaries

Interpret Coaction benchmarks honestly and keep local applications on the transport-free entry point.

Performance claims are useful only when the scenario, runtime, state shape, and package versions are visible. Coaction keeps its benchmark scripts in the repository so results can be regenerated instead of treated as timeless numbers.

Choose the local entry point

For a vanilla local store:

import { create } from 'coaction/local';

This static boundary excludes shared protocol, epoch, reconnect, and transport modules from the entry. The compatibility coaction entry is intentionally broader.

Bundle budgets in the repository measure published entries and tree-shaken consumer fixtures. They are regression gates, not a promise that an application bundle equals the budget number; the final result also depends on external dependencies retained by the application's bundler.

Run the maintained checks

pnpm build
pnpm package:size
pnpm benchmark
pnpm benchmark:zustand-positioning
pnpm benchmark:check
  • package:size checks published entry budgets and core entry isolation.
  • benchmark compares large update paths, including Coaction draft updates and Zustand + Immer.
  • benchmark:zustand-positioning separates stable derived reads from update-then-read work.
  • benchmark:check enforces regression floors with CI headroom.

Thresholds must not be lowered merely to make a failing run green. A threshold change needs a reviewed runtime-semantic or benchmark-methodology reason.

Understand the derived-state tradeoff

Cached accessors make repeated stable reads extremely cheap. After an update, Coaction still must preserve the readonly public-state boundary and update the snapshot used by computed traversal. That makes “update then read a derived value” a different path from a stable cached read.

Do not publish one of those cases as a universal “faster than Zustand” claim. Zustand can make a derived read constant-time by storing the derived value, but application actions must then keep it consistent. Coaction's proposition is maintained cached derivation, not winning every mutation microbenchmark.

Render performance

React's main Coaction-specific optimization is observer() field tracking. Explicit useStore(selector) is broadly comparable to Zustand's selector path. Measure render counts in the actual component tree before converting every component.

Tracking a nested object happens through its containing store/slice field. If sibling changes in a large nested record invalidate too much UI, use an explicit selector for the nested leaf or split the state at a meaningful ownership boundary.

Practical checklist

  • Import coaction/local for vanilla local-only state.
  • Keep patches off unless shared mode or an integration needs them.
  • Use cached getters for repeated derived reads, not side-effectful work.
  • Use observer() where it removes real selector composition.
  • Profile production builds and representative state, not development mode alone.
  • Treat benchmark floors as regression detection and rerun publishable numbers on the target machine.

On this page