Client-Side Tooling · 5 of 8

State Management

How a UI shares data across components without prop-drilling chaos. The most fashionable corner of frontend — every framework has its own family of stores.

Local vs GlobalServer vs ClientReactivityImmutability
← Back to Client Side
Quick Facts

At a Glance

Basic Concepts

  • Local state belongs to a component (a checkbox, a form input).
  • Shared state is read by many components (current user, theme, cart).
  • Server state is data fetched from APIs — needs caching, revalidation, deduplication.
  • URL is state too — search filters, tabs, modal-open belong in the address bar.
  • Don't reach for global state on day one. Lift it; pass props; use context. Reach for a store when prop-drilling actually hurts.
Landscape

The Major Libraries

LibraryFrameworkSweet spot
React ContextReactBuilt-in, low-frequency shared state (theme, auth).
ZustandReact (mostly)Tiny, hook-based global store; minimal boilerplate.
Redux Toolkit (RTK)React + othersBattle-tested for large apps; strict patterns + devtools.
Jotai / RecoilReactAtomic state — fine-grained reactivity.
MobXReact, Vue, anywhereObservable objects, automatic re-render.
PiniaVueOfficial Vue store (replaced Vuex).
NgRx / NGXS / SignalsAngularRedux pattern for Angular; Signals for fine-grained reactivity.
Svelte stores / RunesSvelteBuilt-in; Svelte 5 Runes are the modern default.
TanStack Query (React Query)AnyServer-state caching — fetching, revalidation, mutations.
SWRReactVercel's lighter server-state library.
Apollo Client / urqlAnyGraphQL clients with built-in normalized cache.
XStateAnyState machines & statecharts for complex flows.
Mechanics

The Right Tool for the Job

Server State vs Client State

The single biggest insight of the last 5 years: most "global state" was actually server data being mismanaged.

  • Server state (API data) — use TanStack Query or SWR. Caching, revalidation, optimistic updates, retries are solved problems.
  • Client state (UI flags, drafts) — Zustand, Jotai, or just React state.
  • Trying to put server data in Redux usually leads to a mountain of action types and stale data bugs.
Three Patterns of Reactivity
  • Flux / Redux — explicit actions → reducers → new state. Predictable, verbose.
  • Hook / Store — components call useStore(selector). Modern, ergonomic. Zustand is the prototype.
  • Signals / Atoms — fine-grained reactive primitives. Solid, Svelte 5 Runes, Angular Signals, Jotai, Vue refs.
The Selector Pattern

Subscribe a component only to the slice of state it cares about — re-renders only when that slice changes.

// Zustand
const userName = useStore(s => s.user.name);   // re-renders only when name changes

Without selectors, every component re-renders on every state change — back to square one.

Devtools & Time Travel

Redux Devtools (also works with Zustand & many others) lets you replay actions, inspect state diffs, and reproduce bugs deterministically. Essential for hard-to-reproduce UI bugs.

State Machines (XState)

For complex flows (multi-step forms, video players, async sequences), explicit state machines beat ad-hoc booleans. XState models your UI as states + events + transitions; impossible states become impossible.

Picking

Which to Choose

API Data

TanStack Query (React) or SWR — almost always.

Small App / Side Project

useState + Context. Skip global state.

Medium SPA

Zustand (React) / Pinia (Vue) / Svelte stores. Lightweight.

Large Enterprise SPA

Redux Toolkit (React) / NgRx (Angular). Strict patterns scale.

Complex Flows

XState — wizards, payment flows, players.

Real-time Collaborative

Yjs, Liveblocks, Replicache — CRDTs & sync engines.

Continue

Other Client-Side Tooling