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'
2
3openOverlay('cart') // from anywhere

What ships

createStore / useStoreThe primitive, for a domain of your own
useCart and friendsCart state, addToCart, changeLine, removeLine
openOverlay / useOpenOverlayWhich drawer or modal is open
useShared / usePublishKeyed state, no module to write — see Talking to other sections

Writing a store

1import { createStore, useStore } from '@/framework'
2
3const wishlistStore = createStore({ ids: [], status: 'idle' })
4
5export function useWishlist() {
6 return useStore(wishlistStore, (state) => state.ids)
7}
8
9export 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

useStore selectors must return an Object.is-stable value or React re-renders forever.

1useStore(cartStore, (s) => s.cart.item_count) // fine, a primitive
2useStore(cartStore, (s) => s.cart) // fine, the same reference
3useStore(cartStore, (s) => s.cart.items ?? []) // loops: a new [] every call

The 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.