Skip to content

Comparison

Anyone who has reached for XState or Zag will feel at home — same statechart vocabulary (states, transitions, guards, actions, effects), same headless philosophy. Those libraries are excellent; this one exists for two things they aren’t built around: no environment assumption and performance under heavy fan-out.

CapabilityZagXStateDunky
States / transitions / guards
Guard combinators (and/or/not)
entry / exit
Conditional actionschoosechooseoneOf
Effects with cleanupeffects ¹invoked callbackseffects
Computed / derived state
Timed transitions (after)
Watch (react to a data change)watchvia alwayswatch
Per-platform late bindingvia .provide()ComponentEffect

¹ Zag’s effects receive a scope (a DOM abstraction) and reach for it. Dunky effects receive no environment — they’re platform-free by design.

The single cause underneath all the differences is how each engine holds a machine’s data.

ZagXStateDunky
Data modelreactive cell per fieldimmutable snapshot per eventone plain object, mutated in place
Fine-grained subscriptions❌ host framework does it⚠️ actor.select (coarse)select (value-deduped)
Runs without a host framework❌ presumes a DOM framework⚠️ yes, but DOM-tied packages✅ any JS runtime
Environment assumptionDOMDOMnone (DOM, RN, TUI, WebGL…)
The machine never sees propsprop() inside the machine.provide() inline✅ props live at the edge only
Serializable snapshot❌ state is scattered hook cells✅ actor model⚠️ no built-in (context is one object)
Nested / hierarchical states❌ by design❌ flat
Parallel states❌ by design⚠️ compose (peers, no shared event bus)
Spawned child actors❌ by design❌ by design

XState allocates a new immutable snapshot on every transition — the right trade for time-travel and serialization, but it taxes the hot path. Dunky mutates in place behind a value-deduping notifier, so a transition allocates nothing and an unchanged field wakes no observer.

Zag pioneered the headless component-as-machine idea and can run outside React, but it presumes a host DOM framework and puts reactive cells (one per context field) into framework hooks. Dunky owns its reactivity internally — the same machine runs on the DOM, React Native, or any other JS runtime.

Reach for…When you need…
XStateNested/parallel statecharts, actor model, time-travel, Stately Studio visual editor
ZagReady-made headless component machines (accordion, combobox, …) for web
DunkyMany lightweight UI machines at high event frequency, or the same machine on multiple platforms (web + native)

Dunky is built for density × frequency — many machines reacting to a high-frequency stream inside one frame budget (trading terminals, canvas boards, monitoring walls, game HUDs). In practice that’s up to ~8× the event throughput of the alternatives, flat memory as context grows wide, and surgical re-renders that wake only the rows that actually changed.

See the benchmark for methodology and full tables.