Global Store
Package
store ships in the separate olum-store package (already included in the starter template):
npm i olum-store
olum auto-loads it, so there's nothing else to set up; if the package is removed, calling
store() throws "store is unavailable" and the rest of the framework keeps working.
Component state lives and dies with its component. For state shared across components — a cart, the signed-in user, app settings — Olum ships a zustand-style global store: define it once in a plain .js module, import it anywhere, and every component that reads it re-renders automatically when it changes.
import { store } from "olum"; export const cart = store({ items: [], add(name) { this.items.push(name); }, clear() { this.items = []; }, });
State and the actions that mutate it live in the same object — methods reach the state through this. Then any component, in any file, imports it like a normal module. One component writes:
<script> import { cart } from "./store"; const products = ["Compass", "Canteen", "Rope"]; </script> <for each="product of products"> <button onclick="cart.add(product)">Add {product}</button> </for>
…and a different component reads — always in sync, with no props and no event forwarding:
<script> import { cart } from "./store"; </script> <p> <strong>{cart.items.length}</strong> {cart.items.length === 1 ? "item" : "items"} in the basket </p> <if when="cart.items.length"> <ul> <for each="name of cart.items"> <li>{name}</li> </for> </ul> <button onclick="cart.clear()">Empty basket</button> </if>
No providers, no wiring: a component subscribes by reading the store during render. When the store changes, every subscribed component still in the DOM re-renders; components that only write (and never read) are untouched.
A store module is just a .js file — only page.html files become routes, so keep it wherever it reads best: next to the pages that share it, or in utils/ for app-wide stores.
Actions
Use method shorthand (add() {}), not arrow functions — arrows don't bind this, so they can't reach the store's state:
export const cart = store({ items: [], add(item) { this.items.push(item); }, // ✓ method — `this` is the store clear: () => (this.items = []), // ✗ arrow — `this` is NOT the store });
Mutating from outside an action works too — cart.items = [] in a component handler is fine. Actions are a convention for keeping the logic next to the data, not a requirement.
store(init) takes a plain object — the same rule as component state. Class instances aren't accepted as the store root.
One store per concern beats one giant store: each is just an export, so split freely (cart.js, session.js, settings.js) and let components import only what they read.