Seitu

Type-safe reactive primitives with one contract: get(), set(), subscribe(). In-memory state, validated localStorage and IndexedDB, media queries and scroll position — inside components or far away from them.

pnpm add seitu
// Import needed function, e.g. local storageimport { createWebStorage } from 'seitu/web'// Use any Standard Schema library you wantimport * as z from 'zod'// Create an instance of the functionconst localStorage = createWebStorage({  type: 'localStorage',  schemas: { count: z.number(), name: z.string() },  defaultValues: { count: 0, name: '' },})

Learn one API, use it everywhere

A store, a validated localStorage key, an IndexedDB table and a media query all hand you the same three methods. Swap the source without rewriting the code around it.

const value = createStore(0) // or storage, media query, scroll state…value.get() // read anywhere, no component neededvalue.set(v => v + 1) // write, sync or asyncvalue.subscribe(console.log) // listen, returns an unsubscribe

One contract

Every primitive returns the same handle: get(), set() and subscribe(). Learn it once, use it for state, storage and browser APIs.

Validated by your schema

Pass any Standard Schema validator — Zod, Valibot, ArkType. Stored data is parsed on read and write, and falls back to your defaults.

Usable outside components

Primitives live at module scope, so utilities, event handlers and tests read the same value your components render.

SSR-safe

Browser APIs are touched only after mount, so servers get your default values instead of a hydration mismatch.

Framework bindings

A useSubscription hook, composable or rune for React, Vue, Solid and Svelte — with selectors to skip needless re-renders.

Import only what you use

Entry points per area and per feature: seitu/web for everything browser, seitu/web/web-storage for just one.

Pick your framework

The primitives never change. Only the way you subscribe to them does.

Read the docs