Comparisons

Pairs that get conflated in real conversations, and in real pull requests. Neither column wins — what decides is the requirement. Each record leads with the confusion, because the confusion is the reason the record exists.

Server state vs Client state

What people get wrong about this pair

The most consequential state mistake in frontend work is putting server state in a client store and then wondering why so much code is about keeping it correct. The two have different lifecycles and need different machinery. Server state is shared, it goes stale on its own, it can be modified by another user or another tab, it needs revalidation, deduplication, cancellation and a retry policy, and two responses to it can arrive out of order. Client state has none of those properties: it cannot be stale, nobody else can change it, and it does not need to be invalidated. When you model the first as the second, you end up hand-writing a cache — invalidation, background refresh, race handling, optimistic rollback — inside reducers, one endpoint at a time. The corollary is also true and less often said: a query cache is a bad home for genuinely local UI state, because you have wrapped a value that can never be stale in a system built entirely around staleness.

Server state (a cached copy of something the server owns)
Use it when

Anything fetched: records, lists, search results, the current user's profile. You are holding a copy of a value another system is free to change without telling you.

Client state (something the browser genuinely owns)
Use it when

Anything that exists only because this browser is open: which tab is selected, whether a menu is expanded, the contents of a field being typed into, a theme preference.

DimensionServer state (a cached copy of something the server owns)Client state (something the browser genuinely owns)
Source of truthThe server; you hold a copyThis browser tab
Can go staleYes, silently, at any momentNo — there is nothing to be stale against
Needed machineryKeys, invalidation, revalidation, dedup, cancellation, retriesA variable with an owner
RacesYes — responses arrive out of orderEffectively none
Shared withOther users, other tabs, other devicesNothing outside this tab unless you persist it
Right homeA query cache keyed by the requestThe component that owns it, or the URL if it is shareable
Symptom of getting it wrongReducers full of hand-written cache logicA global store nobody can reason about