Comparison with XState
Bundle Size
effstate is significantly smaller than XState, making it ideal for bundle-conscious applications.
| Library | Minified | Minified + Gzipped |
|---|---|---|
| effstate | ~14 kB | ~3.9 kB |
| XState | 45.3 kB | 13.7 kB |
effstate is ~3.5x smaller than XState (gzipped).
Performance Benchmarks
We benchmark both libraries with equivalent state machine implementations to ensure fair comparison.
Machine Creation
Creating a new state machine definition.
| Library | ops/sec | Mean (μs) |
|---|---|---|
| effstate | 13,723,258 | 0.073 |
| XState | 268,803 | 3.720 |
effstate is ~51x faster at machine creation.
Actor Lifecycle
Creating, starting, and stopping an actor.
| Library | ops/sec | Mean (μs) |
|---|---|---|
| effstate | 57,627 | 17.353 |
| XState | 307,461 | 3.252 |
XState is ~5x faster at actor lifecycle. This is the cost of effstate’s Effect-first architecture - actor creation captures the runtime for dependency injection. Use interpretManual() if you need maximum performance and manage cleanup yourself.
Event Sending (1000 events)
Sending 1000 events to a running actor.
| Library | ops/sec | Mean (μs) |
|---|---|---|
| effstate | 15,166 | 65.9 |
| XState | 610 | 1640.7 |
effstate is ~25x faster at event processing.
With Subscribers (5 subscribers, 100 events)
Processing events with multiple subscribers attached.
| Library | ops/sec | Mean (μs) |
|---|---|---|
| effstate | 44,536 | 22.5 |
| XState | 5,728 | 174.6 |
effstate is ~8x faster with subscribers.
Realistic App Lifecycle
Complete workflow simulating real app usage: create actor → subscribe (like React component) → 50 user interactions → unsubscribe → stop.
| Library | ops/sec | Mean (μs) |
|---|---|---|
| effstate | 54,743 | 18.3 |
| XState | 11,323 | 88.3 |
effstate is ~5x faster for realistic app lifecycles.
Summary
| Benchmark | Winner | Factor |
|---|---|---|
| Machine Creation | effstate | 51x faster |
| Actor Lifecycle | XState | 5x faster |
| Event Sending | effstate | 25x faster |
| With Subscribers | effstate | 8x faster |
| Realistic Lifecycle | effstate | 5x faster |
Final Score: effstate 4 - 1 XState
Why the Difference?
XState has additional overhead from:
- DevTools/inspection support (always-on)
- Actor system relay for distributed event routing
- Full Observable protocol (next/error/complete)
effstate is optimized for:
- Minimal runtime overhead for event processing
- Simple callbacks with error isolation
- Effect ecosystem integration
- Hierarchical parent/child actor communication
Performance Tips
For maximum performance:
- Use
interpretManual()instead ofinterpret()when you manage actor lifecycle manually - Pre-create event instances instead of creating new ones each time
- effstate’s event processing is 25x faster, so focus on minimizing actor creation if needed
Running the Benchmarks
You can run the benchmarks yourself:
git clone https://github.com/handfish/effstatecd effstatepnpm installpnpm --filter effstate benchThe benchmark code is in packages/core/bench/machine.bench.ts.