Skip to content
asim.dev
May 4, 20265 min read

Pragmatic State Management: Moving Beyond Monolithic Global Stores

Job Radar's dashboard taught me to stop asking 'which store?' and start asking 'who owns this state, and where does it live?'

Architectural separation of server caching and client UI state in React

Every React project reaches the ceremony where someone proposes The Store. Job Radar — my automated job-hunting dashboard — has every ingredient for store sprawl: server data (jobs, search logs), session data (keywords, filters), UI state (panels, selections), and a form or two. Here's the structure that kept it boring, in the best sense.

Question one: is it server state or client state?

Most 'global state' in a dashboard is actually server cache — data the client mirrors but doesn't own. Jobs, logs, keyword lists: those live on the server, belong to RSC data fetching (or TanStack Query where client-side mutation matters), and enter components as props. The remaining client state is usually tiny — which panel is open, which job is selected, a draft filter:

ts
// Server state: fetched, cached, owned by the server
const jobs = await getJobs({ status: "new" });

// UI state: one tiny store, colocated with its feature
const useFilterPanel = create<FilterState>((set) => ({
  open: false,
  draft: null,
  toggle: () => set((s) => ({ open: !s.open })),
}));

Rules that survived contact with the app

  • Server state gets cache semantics. Stale-while-revalidate, invalidation after mutations — not useState mirrors of the database.
  • Client state gets scoped stores. Zustand slices colocated with features; nothing global unless three features genuinely read it.
  • URL is state. Filters and selected-job live in search params — deep-linkable, back-button-friendly, free persistence.
  • Forms own themselves. Local component state until submit; lifting form drafts into global scope is how stores metastasize.

The result

Job Radar's 'store' is three feature slices totaling maybe sixty lines, a query layer that already existed, and a URL that remembers what you were looking at. The monolithic global store isn't wrong — it's just answering the wrong question. Ask who owns this state first, and most of the store disappears.