README.md
February 13, 2026 ยท View on GitHub
StateLoom
Universal state management SDK for JavaScript and TypeScript
One reactive core. Three paradigms. Every framework.
StateLoom provides a signal-based reactive core with paradigm adapters (Store, Atom, Proxy) and framework adapters (React, Vue, Solid, Svelte, Angular). Pick the mental model you prefer, use it in any framework, and compose middleware for persistence, devtools, sync, and more.
Why StateLoom?
- Signal-based core (~1.5 KB gzipped) -- push-pull hybrid reactivity aligned with the TC39 Signals proposal
- Three paradigms, one core -- Store (Zustand-style), Atom (Jotai-style), Proxy (Valtio-style) all built on the same signal graph
- Every major framework -- React, Vue, Solid, Svelte, Angular with thin, idiomatic adapters
- SSR-first -- per-request scope isolation prevents state leakage between server requests
- Composable middleware -- persistence, devtools, tab sync, history, telemetry as independent packages
- TypeScript-first -- strict types with full inference, no
any, no enums, named exports only - Tree-shakeable -- pay only for the packages you use
Paradigm Comparison
| Store | Atom | Proxy | |
|---|---|---|---|
| Mental model | Single object with actions | Bottom-up composition | Mutable syntax, immutable snapshots |
| Inspired by | Zustand, Redux Toolkit | Jotai, Recoil | Valtio, MobX |
| Best for | App-wide state with actions | Fine-grained, derived state | Rapid prototyping, mutable APIs |
| Middleware | Yes | No | No |
| Package | @stateloom/store | @stateloom/atom | @stateloom/proxy |
Quick Start
Store (Zustand-style)
import { createStore } from '@stateloom/store';
const counterStore = createStore((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
reset: () => set({ count: 0 }),
}));
counterStore.getState().increment();
console.log(counterStore.getState().count); // 1
Atom (Jotai-style)
import { atom, derived } from '@stateloom/atom';
const countAtom = atom(0);
const doubledAtom = derived((get) => get(countAtom) * 2);
countAtom.set(5);
console.log(doubledAtom.get()); // 10
Proxy (Valtio-style)
import { observable, snapshot } from '@stateloom/proxy';
const state = observable({ count: 0, text: 'hello' });
state.count++;
const snap = snapshot(state);
console.log(snap.count); // 1
Core Signals (Low-level)
import { signal, computed, effect } from '@stateloom/core';
const count = signal(0);
const doubled = computed(() => count.get() * 2);
effect(() => {
console.log(`Count is ${count.get()}, doubled is ${doubled.get()}`);
});
count.set(5); // logs: "Count is 5, doubled is 10"
Framework Support
| Framework | Package | Adapter Pattern |
|---|---|---|
| React | @stateloom/react | useSignal(), useStore(), ScopeProvider |
| Vue | @stateloom/vue | useSignal(), useStore(), stateloomPlugin |
| Solid | @stateloom/solid | useSignal(), useStore(), ScopeProvider |
| Svelte | @stateloom/svelte | toReadable(), toWritable() |
| Angular | @stateloom/angular | toAngularSignal(), injectStore(), toObservable() |
| Vanilla JS | @stateloom/core | Direct signal/computed/effect API |
Packages
Core
| Package | Description |
|---|---|
@stateloom/core | Signal-based reactive primitives (signal, computed, effect, batch, scope) |
Paradigm Adapters
| Package | Description |
|---|---|
@stateloom/store | Zustand-style store with actions and middleware support |
@stateloom/atom | Jotai-style atomic state with derived atoms and atom families |
@stateloom/proxy | Valtio-style proxy objects with structural sharing snapshots |
Framework Adapters
| Package | Description |
|---|---|
@stateloom/react | React hooks and SSR scope provider |
@stateloom/vue | Vue composables and plugin |
@stateloom/solid | SolidJS primitives and scope context |
@stateloom/svelte | Svelte readable/writable store adapters |
@stateloom/angular | Angular signals, observables, and DI integration |
Middleware and Ecosystem
| Package | Description |
|---|---|
@stateloom/devtools | Browser DevTools integration and logging middleware |
@stateloom/persist | State persistence (localStorage, sessionStorage, IndexedDB, cookies) |
@stateloom/persist-redis | Redis storage backend for @stateloom/persist |
@stateloom/history | Undo/redo history middleware |
@stateloom/tab-sync | Cross-tab state synchronization via BroadcastChannel |
@stateloom/immer | Immer integration for immutable updates with mutable syntax |
@stateloom/telemetry | State change telemetry and error tracking middleware |
@stateloom/server | Server-side scope management with LRU caching |
@stateloom/testing | Test utilities (mock stores, test scopes, value collectors) |
Installation
Install the core and the packages you need:
# Core + Store + React (most common setup)
pnpm add @stateloom/core @stateloom/store @stateloom/react
# Core + Atom (bottom-up composition)
pnpm add @stateloom/core @stateloom/atom
# Core + Proxy (mutable syntax)
pnpm add @stateloom/core @stateloom/proxy
# Add middleware as needed
pnpm add @stateloom/persist @stateloom/devtools
Documentation
- Getting Started -- set up StateLoom in your project
- API Reference -- per-package documentation
- Architecture -- design decisions and layer diagram
- Contributing -- how to contribute
Examples
| Example | Framework | Source |
|---|---|---|
| Vanilla JS | None | examples/vanilla-js |
| React + Vite | React 19 | examples/react-vite |
| Vue + Vite | Vue 3 | examples/vue-vite |
| Solid + Vite | SolidJS | examples/solid-vite |
| Svelte + Vite | Svelte 5 | examples/svelte-vite |
| Angular | Angular 19 | examples/angular |
| Next.js SSR | Next.js 15 | examples/nextjs-ssr |
Contributing
We welcome contributions! See CONTRIBUTING.md for how to get started.
For detailed contributor documentation, see the Contributing Guide.
License
MIT