Stores
Create an Immer-backed store and mutate it through drafts.
@nice-code/state is a framework-agnostic, Immer-backed state container with fine-grained selector subscriptions, derived reactions, patch streams, and an optional React adapter.
bun add @nice-code/state immer- One immutable store, mutated with Immer — write plain mutations (
s.count += 1), get structural sharing for free. - No-op updates are genuinely free — listeners fire only when the root reference actually changes.
- Fine-grained selectors — components and side-effects re-run only when their watched slice changes structurally.
- Tear-free React — built on
useSyncExternalStore, no provider or context required.
Create a store
Section titled “Create a store”Pass a value, or a factory function (re-used by SSR / reset).
import { Store } from "@nice-code/state";
interface ICounterState { count: number; step: number;}
export const counterStore = new Store<ICounterState>({ count: 0, step: 1 });Update state
Section titled “Update state”Mutations run through Immer — mutate the draft freely; the store commits a new immutable state.
// Single updatecounterStore.update((s) => { s.count += s.step;});
// Batched — applied in order, committed as one changecounterStore.update([ (s) => { s.count += 1; }, (s) => { s.count += 1; },]);
// Replace the whole statecounterStore.replace({ count: 0, step: 1 });
// Replace by mapping from current statecounterStore.replaceFromCurrent((s) => ({ ...s, count: 0 }));The second argument to an updater is a readonly snapshot of the pre-update state:
counterStore.update((draft, original) => { draft.count = original.count * 2;});Read state
Section titled “Read state”const { count } = counterStore.getRawState();Reach for
getRawState()only in plain logic. In components useuseStoreState; for side-effects usewatch.
useStoreState renders the store’s current value on the server without subscribing. Construct stores with a factory (new Store(() => initialState())) so each request can seed its own state.
Next: React adapter →