Skip to content

Latest commit

 

History

History
195 lines (144 loc) · 8.25 KB

File metadata and controls

195 lines (144 loc) · 8.25 KB

Getting Started

StateLoom is a universal state management SDK for JavaScript and TypeScript. It provides a signal-based reactive core with three paradigm adapters -- Store, Atom, and Proxy -- and framework adapters for React, Vue, Solid, Svelte, and Angular.

Why StateLoom?

  • One core, any paradigm: Choose the mental model that fits your team -- Zustand-like stores, Jotai-like atoms, or Valtio-like proxies. Switch or mix without rewriting your app.
  • Framework-agnostic: The reactive core runs anywhere JavaScript runs. Framework adapters are thin bridges, not heavy abstractions.
  • Composable middleware: Add persistence, devtools, tab sync, and undo/redo via a standard middleware interface.
  • Tiny core: @stateloom/core targets ~1.5 KB gzipped with zero platform dependencies.

Architecture at a Glance

graph TB
    subgraph Core["@stateloom/core"]
        signal["signal()"]
        computed["computed()"]
        effect["effect()"]
        batch["batch()"]
    end

    subgraph Paradigms["Pick a Paradigm"]
        Store["@stateloom/store<br/>Zustand-like"]
        Atom["@stateloom/atom<br/>Jotai-like"]
        Proxy["@stateloom/proxy<br/>Valtio-like"]
    end

    subgraph Adapters["Pick a Framework"]
        React["@stateloom/react"]
        Vue["@stateloom/vue"]
        Solid["@stateloom/solid"]
        Svelte["@stateloom/svelte"]
        Angular["@stateloom/angular"]
    end

    Core --> Store
    Core --> Atom
    Core --> Proxy

    Store --> React
    Store --> Vue
    Store --> Solid
    Store --> Svelte
    Store --> Angular
Loading

Choose Your Paradigm

StateLoom offers three paradigm adapters. Each wraps the same signal-based core but provides a different API style:

Store Atom Proxy
Package @stateloom/store @stateloom/atom @stateloom/proxy
Mental model Single object with actions Composable bottom-up atoms Direct mutation
Inspired by Zustand Jotai Valtio
Best for Feature-scoped state with actions Fine-grained, composable state Rapid prototyping, mutable-style
Middleware Yes No No
TypeScript Inferred from initializer Inferred from atom type Inferred from object shape
flowchart LR
    Q{"How do you think\nabout state?"}
    Q -->|"Object with actions"| Store["Use Store"]
    Q -->|"Small composable pieces"| Atom["Use Atom"]
    Q -->|"Direct mutation"| Proxy["Use Proxy"]
Loading

::: tip Not sure which to choose? Start with Store. It covers most use cases, supports middleware (persistence, devtools, tab sync), and is the most familiar pattern for developers coming from Zustand or Redux. You can always add atoms or proxies alongside it later. :::

Installation

Install the core package plus your chosen paradigm and framework adapter:

::: code-group

# Core + Store + React
pnpm add @stateloom/core @stateloom/store @stateloom/react
# Core + Store + React
npm install @stateloom/core @stateloom/store @stateloom/react
# Core + Store + React
yarn add @stateloom/core @stateloom/store @stateloom/react

:::

Replace @stateloom/react with your framework adapter (@stateloom/vue, @stateloom/solid, @stateloom/svelte, or @stateloom/angular). You can also use the core directly without any framework adapter -- see Vanilla JS.

Quick Start: Signals (Core)

Signals are the fundamental reactive primitive. No paradigm adapter needed:

import { signal, computed, effect } from '@stateloom/core';

const count = signal(0);
const doubled = computed(() => count.get() * 2);

effect(() => {
  console.log(`Count: ${count.get()}, Doubled: ${doubled.get()}`);
  return undefined;
});

count.set(1); // logs: Count: 1, Doubled: 2
count.set(5); // logs: Count: 5, Doubled: 10

Quick Start: Store

Stores combine state and actions in a single object:

import { createStore } from '@stateloom/store';

const counterStore = createStore((set) => ({
  count: 0,
  increment: () => set((s) => ({ count: s.count + 1 })),
  decrement: () => set((s) => ({ count: s.count - 1 })),
  reset: () => set({ count: 0 }),
}));

counterStore.getState().increment();
console.log(counterStore.getState().count); // 1

Quick Start: Atom

Atoms are small, composable pieces of state:

import { atom, derived } from '@stateloom/atom';

const countAtom = atom(0);
const doubledAtom = derived((get) => get(countAtom) * 2);

Quick Start: Proxy

Proxies let you write state with regular mutations:

import { observable, observe } from '@stateloom/proxy';

const state = observable({ count: 0, name: 'Alice' });

observe(() => {
  console.log(`${state.name}: ${state.count}`);
});

state.count++; // logs: Alice: 1

Framework Guides

Choose your framework to see a complete integration guide:

Framework Guide Example App
Vanilla JS/TS Vanilla JS examples/vanilla-js/
React + Vite React + Vite examples/react-vite/
Next.js (SSR) Next.js examples/nextjs-ssr/
Vue 3 Vue examples/vue-vite/
SolidJS SolidJS examples/solid-vite/
Svelte Svelte examples/svelte-vite/
Angular Angular examples/angular/
Astro Astro Coming soon

Migrating from Other Libraries

StateLoom's paradigm adapters are designed to be familiar:

Coming from Use Key differences
Zustand @stateloom/store Same createStore(set => ...) API. Middleware uses an array instead of function wrapping.
Jotai @stateloom/atom Same atom() / derived() pattern. derived replaces Jotai's atom with a read function.
Valtio @stateloom/proxy Same observable() / snapshot() API. observe() replaces subscribe().
Redux/RTK @stateloom/store Replace slices with stores. No reducers needed -- use set() directly.
Pinia @stateloom/store Replace defineStore with createStore. Actions are in the same object.

Next Steps