State Management: A Complete Guide
“What should we use for state management?” is usually the wrong first question. The right one is: what kinds of state does this app have? Most apps have four, and each kind has a natural home.
The four kinds of state
Local interface state — an open menu, a text field draft, a hover. It lives and dies with one component and belongs inside it.
Shared client state — the theme, the signed-in user, the contents of a cart. Several distant components read and write it, so it needs a home they can all reach.
Server state is not client state
The biggest modern insight is that data fetched from a server is its own category. It has a lifecycle — fresh, stale, refetching, failed — that a plain store does not model. Treating a server response as ordinary client state is how apps end up with hand-rolled caches and subtle staleness bugs. Use a data-fetching layer that understands caching and invalidation, and an enormous slice of “state management” disappears.
The URL is state too
Filters, tabs, pagination, the selected item — if refreshing the page should restore it, it belongs in the URL. This is the most underused store in frontend development, and it comes with sharing and back-button support for free.
A decision framework
Start every piece of state at the bottom of this ladder and only climb when forced: component state → the URL → a data-fetching cache → a small shared store. Each step up costs indirection, so each needs a concrete justification. Apps that follow the ladder usually discover the shared store at the top holds almost nothing — a user object and a few preferences — and that is a sign of health, not underuse.
Keep the store boring
Whatever shared store you pick, treat it as a database, not a junk drawer: define the shape, restrict who writes, and derive aggressively. The less state you manage, the less state management you need.