Ilha Store
@ilha/store is a zustand-shaped reactive store for ilha apps. It lives outside any island and is backed by alien-signals — the same engine that powers ilha core state — so stores and islands share a single reactive graph with no extra bridging.
How it relates to ilha
ilha state is island-local — signals are scoped to a single component instance. @ilha/store adds a layer of shared global state that any island (or any non-island code) can read and write. A store is a plain object with getState, setState, subscribe, and bind — no context providers, no wrapping components, no framework lock-in.
Install
Quick start
With encapsulated actions:
When to use
ilha's built-in .state() is the right choice when only one island reads and writes a piece of state. Use @ilha/store when state needs to be:
- Shared across multiple islands — e.g. a cart, auth session, or active theme
- Updated from outside an island — e.g. from a WebSocket handler or global event bus
- Persisted or derived globally — e.g. synced to
localStoragevia asubscribelistener
API
createStore(initialState, actions?)
Creates a store. Optionally accepts an actions creator for encapsulating mutations.
The actions creator receives:
Actions are live functions that survive setState calls and are accessible directly on the state object:
store.setState(update)
Merges a shallow partial update. Accepts a plain object or an updater function. Keys not included in the patch are preserved.
store.getState()
Returns the current state snapshot. When state has not changed, returns a stable reference.
store.getInitialState()
Returns the frozen initial state as it was at construction time. Not affected by subsequent setState calls. Useful for reset actions.
store.subscribe(listener)
Subscribes to all state changes. The listener receives (newState, prevState) and is not called on initial subscription. Returns an unsubscribe function.
store.subscribe(selector, listener) — slice subscription
Subscribes to a derived slice. The listener only fires when the selected value changes (compared with Object.is), so unrelated state updates are ignored.
store.bind(el, render)
Reactively renders a store-driven HTML string into a DOM element whenever state changes. The render function may return a plain string or an html\`` tagged template. Runs immediately on call to set initial content.
store.bind(el, selector, render) — slice bind
Only re-renders when the selected slice changes.
Both forms assign to el.innerHTML. Binding two selectors to the same element is allowed — each bind effect is independent, and the last write wins whenever both fire.
effectScope
Re-exported from alien-signals. Runs a setup function inside a reactive scope and returns a stop() function that tears down every subscribe and bind effect registered inside it in one call. Use this when you need to mount and unmount a group of store consumers together.
Usage with ilha islands
The standard pattern is subscribing to a store slice inside an island's .effect() and driving a local signal from it. The .effect() cleanup return keeps things tidy on unmount.
When the store updates, the slice subscription fires, the island signal updates, and ilha re-renders only the affected DOM — no manual wiring needed.
TypeScript
All types are inferred from the initial state and actions creator. Key exported types: