Skip to content

stores & scopes

A store holds state that outlives a single component. Where state is ephemeral (resets on unmount), a store persists for the lifetime of its scope.

export store counterStore {
state count: number = 0
action inc() { count = count + 1 }
}
Compiled React 19 — this is the file in your repo
import { StoreRegistry, __registerStoreRestore, createStoreInstance, type StoreSurface } from "@reactra/store"
export const counterStore = {
name: "counterStore",
kind: "export",
factory: () => createStoreInstance((notify) => {
let count = (0) as number
const inc = () => { count = count + 1 ; notify(); }
__registerStoreRestore("counterStore", (next) => {
let __written = 0
if ("count" in next) { count = next["count"] as typeof count; __written++ }
notify()
return __written
})
return () => ({ count, inc })
}),
}
export type counterStore = StoreSurface<typeof counterStore>
if (import.meta.hot) {
import.meta.hot.accept((newMod) => {
if (!newMod) return
StoreRegistry.replace(newMod.counterStore)
})
}

inject store is the sole way to acquire a store. It has two forms, and the parentheses are the signal:

inject store counterStore // bare = subscribe to it
inject store cartStore({ userId }) // argumented = own its lifecycle
  • Bare inject store X subscribes the component to the store (re-renders on the fields it reads — subscription is per-field, not whole-store).
  • Argumented inject store X({ ... }) makes this component the store’s lifecycle owner and passes its input.

The store name is bound by an import type { counterStore } from "..." at the top of the file.

Stores declare a scope: app (one instance for the whole app), session, or route-scoped. A session store is the usual way to persist component-like state across route navigation.

export store cartStore {
input userId: string
state items: string[] = []
}
Compiled React 19 — this is the file in your repo
import { StoreRegistry, __registerStoreRestore, createStoreInstance, type StoreSurface } from "@reactra/store"
export const cartStore = {
name: "cartStore",
kind: "export",
factory: () => createStoreInstance((notify) => {
let items = ([]) as string[]
__registerStoreRestore("cartStore", (next) => {
let __written = 0
if ("items" in next) { items = next["items"] as typeof items; __written++ }
notify()
return __written
})
return () => ({ items })
}),
}
export type cartStore = StoreSurface<typeof cartStore>
if (import.meta.hot) {
import.meta.hot.accept((newMod) => {
if (!newMod) return
StoreRegistry.replace(newMod.cartStore)
})
}
  • input X: T is data passed in by the owner via the argumented form (inject store cartStore({ userId })).
  • preserved state X is a route-store feature: in a route-scoped store it survives re-instantiation as you navigate away and back. It’s only valid in a route store — a plain app/session store uses ordinary state.

Next: services & inject.