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
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.
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.
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.
| Dimension | Server state (a cached copy of something the server owns) | Client state (something the browser genuinely owns) |
|---|---|---|
| Source of truth | The server; you hold a copy | This browser tab |
| Can go stale | Yes, silently, at any moment | No — there is nothing to be stale against |
| Needed machinery | Keys, invalidation, revalidation, dedup, cancellation, retries | A variable with an owner |
| Races | Yes — responses arrive out of order | Effectively none |
| Shared with | Other users, other tabs, other devices | Nothing outside this tab unless you persist it |
| Right home | A query cache keyed by the request | The component that owns it, or the URL if it is shareable |
| Symptom of getting it wrong | Reducers full of hand-written cache logic | A global store nobody can reason about |