Data
State across islands
Why Context cannot reach, and what does.
Every section is a separate createRoot. React Context can't reach between
them. The cart trigger is in the header, add-to-cart is in a product section,
the drawer is in the overlay group — three trees.
It doesn't need a state library. Everything bundles into one assets/main.js, so
a module-level variable is already shared: the roots differ, the module instance
does not. The only missing piece is telling React to re-render, and React ships
that — useSyncExternalStore. The whole store is about 40 lines.
1import { openOverlay } from '@/framework'23openOverlay('cart') // from anywhereWhat ships
createStore / useStore | The primitive, for a domain of your own |
useCart and friends | Cart state, addToCart, changeLine, removeLine |
openOverlay / useOpenOverlay | Which drawer or modal is open |
useShared / usePublish | Keyed state, no module to write — see Talking to other sections |
Writing a store
1import { createStore, useStore } from '@/framework'23const wishlistStore = createStore({ ids: [], status: 'idle' })45export function useWishlist() {6 return useStore(wishlistStore, (state) => state.ids)7}89export function addToWishlist(id) {10 wishlistStore.setState((state) => ({ ...state, ids: [...state.ids, id] }))11}Export functions rather than the store itself. A store with an exported
setState is a store anyone can put in any shape; a store behind addToWishlist
has a contract.
The one rule
useStoreselectors must return anObject.is-stable value or React re-renders forever.
1useStore(cartStore, (s) => s.cart.item_count) // fine, a primitive2useStore(cartStore, (s) => s.cart) // fine, the same reference3useStore(cartStore, (s) => s.cart.items ?? []) // loops: a new [] every callThe third one looks harmless and is the most common way to hang a page. If you
need a derived array, compute it with useMemo in the component rather than in
the selector.
When to reach for a library
Zustand or Jotai earn their place when you actually need devtools and time-travel, middleware, persistence, or selector memoisation across a large state tree. Not at this size — at this size they are indirection with a bundle cost.