Skip to content

Comparison with XState

Bundle Size

effstate is significantly smaller than XState, making it ideal for bundle-conscious applications.

LibraryMinifiedMinified + Gzipped
effstate~14 kB~3.9 kB
XState45.3 kB13.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.

Libraryops/secMean (μs)
effstate13,723,2580.073
XState268,8033.720

effstate is ~51x faster at machine creation.

Actor Lifecycle

Creating, starting, and stopping an actor.

Libraryops/secMean (μs)
effstate57,62717.353
XState307,4613.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.

Libraryops/secMean (μs)
effstate15,16665.9
XState6101640.7

effstate is ~25x faster at event processing.

With Subscribers (5 subscribers, 100 events)

Processing events with multiple subscribers attached.

Libraryops/secMean (μs)
effstate44,53622.5
XState5,728174.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.

Libraryops/secMean (μs)
effstate54,74318.3
XState11,32388.3

effstate is ~5x faster for realistic app lifecycles.

Summary

BenchmarkWinnerFactor
Machine Creationeffstate51x faster
Actor LifecycleXState5x faster
Event Sendingeffstate25x faster
With Subscriberseffstate8x faster
Realistic Lifecycleeffstate5x 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 of interpret() 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:

Terminal window
git clone https://github.com/handfish/effstate
cd effstate
pnpm install
pnpm --filter effstate bench

The benchmark code is in packages/core/bench/machine.bench.ts.