Skip to content

Design Philosophy

StateLoom is a universal state management SDK that unifies the best patterns from the JavaScript state management ecosystem into a single, coherent library. This document explains the core design principles and the reasoning behind every major architectural decision.

Core Thesis

The signal graph is the universal primitive for state management. Every paradigm — stores, atoms, proxies — can be expressed as compositions of signals and computed values. By building on a signal-based reactive core, StateLoom achieves paradigm unification without paradigm compromise.

Design Principles

1. Zero-Barrier Adoption

Developers should be productive within minutes, not hours. StateLoom achieves this by:

  • Familiar APIs: Each paradigm mirrors the API patterns developers already know (Zustand's createStore, Jotai's atom, Valtio's observable).
  • Incremental adoption: Start with any single paradigm. Add others as needed. The core is always the same.
  • No boilerplate: Creating a store is a single function call. No providers required for basic usage. No action types, reducers, or dispatchers.
  • TypeScript-first with inference: Types flow automatically. Developers rarely need to annotate.

2. Minimal Core, Additive Complexity

The core reactive kernel targets ~1.5 KB gzipped. Every feature beyond basic reactivity is a separate, tree-shakeable package:

What you needWhat you installApproximate size
Reactive primitives only@stateloom/core~1.5 KB
Store pattern+ @stateloom/store+~0.5 KB
React bindings+ @stateloom/react+~0.3 KB
Persistence+ @stateloom/persist+~1.0 KB
DevTools+ @stateloom/devtools+~0.8 KB

You pay only for what you use. A React app using the store paradigm with persistence loads roughly 3.3 KB gzipped — competitive with Zustand alone.

3. Framework-Agnostic by Architecture, Not by Afterthought

The entire SDK is designed around a universal Subscribable<T> contract:

typescript
interface Subscribable<T> {
  get(): T;
  subscribe(callback: (value: T) => void): () => void;
}

Every signal, computed, store, and atom implements this interface. Framework adapters are thin bridges (50–200 lines each) that connect this contract to framework-specific reactivity:

  • React: useSyncExternalStore(signal.subscribe, signal.get)
  • Vue: shallowRef wrapper with onScopeDispose cleanup
  • Svelte: Native store contract — signals work with $store syntax directly
  • Solid: createSignal bridge
  • Angular: Observable wrapper via new Observable(subscriber => signal.subscribe(...))

This means the core is genuinely runtime-agnostic: browser, Node.js, Bun, Deno, Web Workers, Service Workers.

4. SSR as a First-Class Primitive

Most state libraries treat SSR (Server-Side Rendering) as an afterthought, leading to the "store singleton in serverless" bug class. StateLoom makes SSR isolation a core primitive via the Scope model, inspired by Effector's Fork API:

  • createScope() creates an isolated state universe
  • runInScope(scope, fn) executes operations within that scope
  • serializeScope(scope) extracts state for client hydration

Every server request gets its own scope. No shared mutable state between requests. No data leakage. This is enforced by design, not convention.

5. Push-Pull Hybrid Reactivity

The reactive core uses the push-pull hybrid algorithm proven by Preact Signals and Angular Signals:

  1. Push phase: When a signal changes, "dirty" marks propagate eagerly through the dependency graph
  2. Pull phase: Computed values only recompute when actually read (lazy evaluation)

This solves the diamond problem without glitches: if node D depends on B and C (both depending on A), D never sees an intermediate state where only B has updated. The push phase marks everything dirty; the pull phase recomputes in the correct order.

6. Composition Over Configuration

Following Zustand's proven middleware pattern, StateLoom uses middleware composition for cross-cutting concerns:

typescript
const store = createStore((set, get) => ({ count: 0 }), {
  middleware: [logger(), devtools({ name: 'Counter' }), persist({ key: 'counter' })],
});

Middleware wraps state operations in a pipeline. Each middleware intercepts set, get, subscribe, and lifecycle events. The type system tracks middleware mutations through the chain, preserving full type safety.

7. TC39 Signals Alignment

The core API aligns with the TC39 Signals proposal (currently Stage 1). When native signals ship in JavaScript engines, StateLoom can delegate to them with zero API changes. This makes the SDK architecturally future-proof — developers invest in learning an API surface that will become a platform primitive.

What StateLoom Is Not

  • Not a state machine library: Use XState for complex statecharts. StateLoom handles reactive data flow.
  • Not a server state cache: Use TanStack Query for server state synchronization. StateLoom handles client-side and isomorphic state.
  • Not a replacement for framework-native state: For simple component-local state, use useState/ref/createSignal. StateLoom is for shared, cross-component, and cross-boundary state.

Principle-to-Implementation Mapping

Each design principle maps to concrete implementation decisions:

Design Trade-off Decisions

Key architectural trade-offs and the rationale behind each choice:

Trade-offChoiceAlternative ConsideredRationale
Push vs pull reactivityPush-pull hybridPure push (MobX), Pure pull (polling)Avoids glitches and unnecessary computation
Signal equalityObject.is defaultDeep equality, === onlyHandles NaN, avoids deep comparison cost
Middleware attachmentArray on createStoreGlobal plugin registryExplicit composition, no hidden global state
Atom state locationConfig object + ScopeValue in atom instanceSSR isolation without recreating atoms
Proxy depthLazy deep proxyingEager proxying at creationAvoids proxying unused subtrees
Effect schedulingSynchronous (batched)Always async (microtask)Predictable ordering, no stale reads
Bundle strategyOne package per concernMonolithic bundleTree-shaking, pay-for-what-you-use
Adapter patternPer-framework packageUniversal HOC/wrapperNative feel per framework, smaller adapters

Design Influences

LibraryWhat StateLoom borrowsWhat StateLoom improves
Preact SignalsPush-pull hybrid reactivity, doubly-linked dependency graphAdds paradigm adapters, middleware, SSR
ZustandMiddleware composition, minimal API surfaceAdds signal core, multi-paradigm, SSR isolation
JotaiBottom-up atom composition, Suspense integrationUnified with store/proxy paradigms via shared core
ValtioProxy-based mutable syntax, structural sharing snapshotsShared reactive core instead of separate implementation
EffectorFork API for SSR isolation, per-request scopingSimplified API, smaller bundle
NanostoresMinimal footprint, framework adapter patternMulti-paradigm support, middleware system
Legend StateDeclarative persistence with plugin-based syncIntegrated into middleware pipeline
TanStack StoreUniversal Subscribable contract for adaptersFull paradigm adapters, not just raw signals

Cross-References