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.