Home/Docs/Store vs State vs Props
Store

Store vs State vs Props

Olum has three ways to get data into a component. They compose — most apps use all three — but each has a clear home turf:

statepropsstore
Scopeone componentparent → childthe whole app
Defined inthe componentthe parent's templatea plain .js module
Who writes itthe component itselfnobody — read-onlyanyone who imports it
Re-render timingsynchronous, per mutationwith the parentmicrotask-batched
Lifetimedies with the componentdies with the childas long as the app runs

Rules of thumb

  • Local first. If only one component cares — an input draft, an open/closed flag — keep it in state. Reaching for the store here just makes private data global.
  • Parent → child is props. Data flowing down one level doesn't need a store; pass it as a prop, and pass a callback prop when the child needs to report back up.
  • Shared or long-lived is the store. When two components with no parent/child relationship need the same data (cart + navbar badge), or the data must survive route changes, put it in a store. Navigating away unmounts a page and its state; a store keeps its value.
  • Prop-drilling is the tell. The moment a prop is passed through a component that doesn't use it, move that data to a store and let the leaf import it directly.

They compose

A page can read a store, derive with plain functions, and hand slices down as props — the child stays store-agnostic and reusable:

Basket.html
<script>
  import { cart } from "./store";
  import Row from "./Row";

  const state = { filter: "" };  // local concern: stays state
  const visible = () => cart.items.filter((i) => i.includes(state.filter));
</script>

<input value="{state.filter}" oninput="(e) => state.filter = e.target.value" />
<for each="item of visible()">
  <Row label="{item}" />
</for>

Because Basket reads cart during render, a cart.add(...) from any other component re-renders it — and its Row children get fresh props for free.