Client State Architecture
Separating server state, client state and URL state — the distinction that removes most frontend complexity.
Definition
Client applications hold several kinds of state with genuinely different properties, and treating them uniformly is the largest single source of avoidable frontend complexity.
The three kinds
Server state. A cached copy of data owned elsewhere. It is stale by default, can change without the client knowing, must be refetched and invalidated, and needs loading and error states.
Treating this as local state produces synchronisation bugs indefinitely: the client believes it owns data it does not own. The correct model is a cache with a staleness policy, not a store.
Client state. Genuinely local UI concerns — is this panel open, what is in this unsent form, which tab is selected. Owned by the client, no synchronisation required.
URL state. Filters, pagination, selected item, search terms. Belongs in the URL because it should be shareable, bookmarkable and restorable by the back button. Putting it in memory breaks those expectations and users notice.
Why the distinction removes complexity
Each kind has a different correct mechanism. Server state wants a caching library with staleness, refetch and invalidation. Client state wants local component state or a light store. URL state wants the router.
Most frontend state management difficulty comes from putting server state in a general-purpose store and then hand-writing cache invalidation, loading states and refetch logic — reimplementing a caching library, badly, in application code.
The decisions that follow
- Staleness policy per data type. A product catalogue can be minutes stale; a cart cannot.
- Optimistic updates where the operation almost always succeeds, with a defined rollback.
- Invalidation on mutation, so a change refreshes what it affects.
- Offline behaviour, if required, which converts server state into a synchronisation problem with conflict resolution — a much larger undertaking that should be entered deliberately.
Failure scenarios
- Server state in a global store, hand-synchronised.
- URL state in memory, so links cannot be shared and the back button misbehaves.
- No staleness policy, so data is either always refetched or never.
- Optimistic updates without rollback, so a failure leaves the interface lying.
- Everything global, so nothing can be reasoned about locally.
Interview question
"What is the difference between server state and client state, and why does conflating them cause bugs?"