Skip to main content

Comparison with Other Libraries

This document compares SoulState with other popular state management libraries based on real benchmark data from our repository. All benchmarks were run with Vitest on identical hardware.


Architecture Overview

Broadcast (Zustand, RTK)

When any state changes, iterate through all subscribers and check each one.

  • Complexity: $O(N)$ where $N$ = total subscribers.
  • Strength: Simple, fast subscriber creation.
  • Weakness: Performance degrades linearly with subscriber count.

Atomic (Jotai, Nanostores)

Each piece of state is a separate atom with its own subscriber list.

  • Complexity: $O(K)$ where $K$ = subscribers to the changed atom.
  • Strength: Natural sparse updates.
  • Weakness: Overhead per atom, limited cross-atom derived state.

Reactive (MobX, Valtio)

Proxy-based reactivity that tracks property access automatically.

  • Complexity: $O(M)$ where $M$ = affected observers.
  • Strength: Fine-grained tracking, minimal boilerplate.
  • Weakness: Proxy overhead, less predictable batching.

Signals (Preact, Solid)

Fine-grained signal primitives with compiler-assisted optimization.

  • Complexity: $O(1)$ per signal update.
  • Strength: Fastest raw update throughput.
  • Weakness: Not a full store solution; limited cross-signal derived state.

DAG (SoulState)

Directed Acyclic Graph with topological propagation and tracking proxy.

  • Complexity: $O(M)$ where $M$ = affected nodes in the graph.
  • Strength: Surgical propagation, glitch-free derived state, no-op elimination.
  • Weakness: Higher store creation overhead.

Benchmark: Sparse Update Performance

Test: 100,000 subscribers across 100 keys. Update 1 key, measure propagation throughput.

RankLibraryArchitectureThroughput (hz)vs SoulState
1Solid SignalsSignal1,349,503408x faster
2Preact SignalsSignal1,237,537374x faster
3NanostoresAtomic13,7934.2x faster
4JotaiAtomic6,9342.1x faster
5SoulStateDAG3,307
6MobXReactive4038.2x slower
7ZustandBroadcast7345x slower
8Redux ToolkitBroadcast4869x slower
ℹ️

Key Insight

Signals (Solid, Preact) dominate raw update throughput because they bypass the store abstraction entirely. Among full store solutions, SoulState is the fastest for sparse updates — 2.1x faster than Jotai, 8.2x faster than MobX, and 45x faster than Zustand.


Benchmark: No-op Elimination

Test: 1,000 subscribers on key a. Update key b (irrelevant change). Measure how fast the library skips unnecessary notifications.

RankLibraryThroughput (hz)vs SoulState
1SoulState248,132
2Jotai176,9311.4x slower
3Zustand32,7107.6x slower
ℹ️

Key Insight

SoulState's DAG detects that no subscriber depends on key b and skips propagation entirely. Zustand still iterates through all 1,000 subscribers to check. Jotai handles this well because atomic updates are naturally scoped.


Benchmark: Same-Value Update Elimination

Test: 1,000 subscribers on key a. Set a to the same value. Measure how fast the library detects and skips the no-op.

RankLibraryThroughput (hz)vs SoulState
1SoulState572,169
2Zustand22,01226x slower

Benchmark: Sparse Update at Scale (100K Subscribers)

Test: 100,000 subscribers. Update key b (no subscriber listens to b). Measure propagation of irrelevant update.

RankLibraryThroughput (hz)vs SoulState
1SoulState189,920
2Zustand263722x slower
⚠️

Critical Difference

When 100K subscribers exist but none are affected, SoulState skips propagation entirely (O(1)). Zustand still iterates through all 100K subscribers (O(N)). This is the 722x gap.


Benchmark: Subscriber Creation

Test: Create 1,000 subscribers on a single-key store. Measure subscription registration speed.

RankLibraryThroughput (hz)vs SoulState
1Zustand4,1521.5x faster
2SoulState2,729
3Jotai8443.2x slower

Test: Create 10,000 subscribers. Measure subscription registration speed.

RankLibraryThroughput (hz)vs SoulState
1Zustand2981.4x faster
2SoulState214
ℹ️

Key Insight

Zustand's simpler subscription model (plain array push) is faster for registration. SoulState pays overhead to build the dependency graph. This is a one-time cost per subscriber — the payoff comes during updates.


Feature Comparison

FeatureZustandJotaiRTKMobXNanostoresPreact SignalsSolid SignalsSoulState
ArchitectureBroadcastAtomicBroadcastReactiveAtomicSignalSignalDAG
Dependency TrackingManualAutomaticManualAutomaticAutomaticAutomaticAutomaticAutomatic
BatchingReactReactReactAutoNoneAutoAutoDeterministic
Derived StateSelectorscomputedSelectorscomputedcomputedcomputedcreateMemocomputed
Glitch-freeNoNoNoNoNoYesYesYes
Vanilla JSYesYesNoYesYesNoNoYes
React IntegrationYesYesYesYesYesPreact onlySolid onlyYes
TypeScriptGoodGoodGoodGoodGoodGoodGoodGood
Bundle Size~1.2 kB~3 kB~12 kB~16 kB~1 kB~2 kB~4 kB~5 kB
DevToolsYesNoYesYesNoNoNoYes
Persist MiddlewareYesNoYesNoNoNoNoYes

When to Choose?

Choose Zustand if:

  • You need the simplest possible API for small-to-medium stores.
  • Subscriber count stays under 5,000.
  • You prioritize minimal bundle size (~1.2 kB).
  • You frequently create/destroy stores (high churn).

Choose Jotai if:

  • You prefer atomic state with automatic dependency tracking.
  • Your state naturally splits into independent atoms.
  • You need good derived state with computed.

Choose MobX if:

  • You want automatic reactivity with minimal boilerplate.
  • You're comfortable with the Proxy-based approach.
  • You need fine-grained reactivity in complex object graphs.

Choose RTK if:

  • You're already in the Redux ecosystem.
  • You need time-travel debugging and middleware.
  • You prefer a predictable, action-based pattern.

Choose Nanostores if:

  • You need the smallest possible bundle (~1 kB).
  • You're building a non-React app (vanilla JS, Preact, etc.).

Choose Solid/Preact Signals if:

  • You're building a Signals-native app (Solid, Preact).
  • You need the fastest possible raw updates.
  • You don't need a full store abstraction.

Choose SoulState if:

  • You're building an enterprise-scale application with 10,000+ active listeners.
  • Your store is large and updates are typically sparse (only affecting a few listeners).
  • You need glitch-free derived state with topological consistency.
  • You need deterministic microtask batching that works outside React.
  • You need no-op elimination (skip propagation when nothing changed).
  • You need high-resolution instrumentation to debug propagation bottlenecks.

Summary: Where SoulState Wins

ScenarioWinnerMargin
Sparse updates at scaleSoulState45x faster than Zustand
No-op eliminationSoulState7.6x faster than Zustand
Same-value updatesSoulState26x faster than Zustand
Irrelevant subscriber skipSoulState722x faster than Zustand
Glitch-free derived stateSoulState, Solid, PreactOnly DAG/Signal libs
Deterministic batchingSoulStateOnly lib with internal scheduler

Summary: Where SoulState Loses

ScenarioWinnerMargin
Subscriber creationZustand1.4x faster
Raw update throughput (signals)Solid Signals408x faster
Bundle sizeNanostores5x smaller
SimplicityZustandSimpler API

ℹ️

Final Thought

SoulState is not a "Zustand killer" — it is a specialized tool for high-density reactive environments. Signals (Solid, Preact) dominate raw throughput because they operate at a lower abstraction level. Zustand wins on simplicity and creation speed. SoulState occupies the middle ground: a full store solution with DAG-powered surgical propagation that scales to 100K+ subscribers where broadcast-based libraries degrade.