Frontend
medium
mid
How would you handle global and local state
Default to local state and lift only when sharing is needed. Distinguish server state (cache it with React Query/SWR) from client state. Use Context for low-frequency global values and a store (Zustand/Redux) for complex, high-frequency global state.
7 min read·~10 min to think through
The core skill is classifying state and putting each kind in the cheapest place that works.
Step 1 — Classify the state
- Local UI state — input values, toggles, hover, "is this dropdown open." Belongs in the component (
useState/useReducer). - Shared client state — cross-component app state like theme, auth user, cart. Global, but client-owned.
- Server state — data that lives in a database and you're caching. The trap: people stuff this in Redux and hand-roll caching/loading/refetch. Don't.
- URL state — filters, current tab, pagination. The URL is shared, bookmarkable, refresh-safe state — use it.
- Form state — often deserves its own tool (React Hook Form) rather than a dozen
useStates.
Step 2 — Pick the mechanism
- Local first. Start with
useStatein the component. Lift only when a second component genuinely needs it. - Lift to nearest common parent before reaching for anything global. Most "global state" is just state lifted too far.
- Context for low-frequency global values (theme, locale, current user). Beware: every consumer re-renders on change, so don't put fast-changing values in one big context — split contexts or use a store.
- Store (Zustand, Redux Toolkit, Jotai) for complex global client state with frequent updates, derived values, or middleware needs. Selectors let components subscribe to slices.
- Server cache (React Query / SWR / RTK Query) for server state — gives you caching, dedup, background refetch, stale-while-revalidate for free.
- URL for anything that should survive a refresh or be shareable.
Decision flow
ts
Does it come from the server? ............ React Query / SWR
Should it survive refresh / be shareable? URL params
Used by one subtree only? ................ local state, maybe lifted
Global, changes rarely? .................. Context
Global, complex / changes often? ......... Zustand / Redux ToolkitAnti-patterns to avoid
- One giant global store holding everything → unnecessary re-renders, hard to reason about.
- Server data in Redux with hand-rolled loading flags → reinventing a cache, badly.
- Prop drilling 5 levels → that's a signal to use Context or composition, not to globalize.
- Putting fast-changing values (mouse position, scroll) in Context.
Follow-up questions
- •Why is server state different from client state, and what breaks if you treat them the same?
- •When does Context cause performance problems, and how do you fix it?
- •Why is the URL underrated as a state container?
- •Zustand vs Redux Toolkit vs Jotai — when each?
Common mistakes
- •Treating server data as client state and hand-rolling caching/refetch logic.
- •Reaching for global state when lifting to a common parent would do.
- •Putting fast-changing values in a single large Context, re-rendering every consumer.
- •Ignoring the URL as the natural home for filters/tabs/pagination.
Performance considerations
- •Context re-renders all consumers on any change — split contexts or use selector-based stores for hot values. Server-cache libraries dedupe requests and avoid waterfalls. Keep derived data computed (useMemo/selectors) instead of stored to avoid sync bugs and stale renders.
Edge cases
- •State that's both server-derived and locally editable (optimistic updates).
- •Cross-tab synchronization of global state.
- •SSR/hydration — server and client initial state must match.
- •Derived state that should be computed, not stored.
Real-world examples
- •React Query for product/listing data, Zustand for cart, Context for theme, URL for active filters — all in one app.
- •Migrating a Redux app's server data to React Query and watching the store shrink by 70%.
Senior engineer discussion
The senior signal is the server-vs-client distinction — recognizing that most 'state management pain' is actually missing cache management, solved by React Query/SWR. They also treat the URL as first-class state, default to colocation, and discuss Context's re-render model rather than naming a favorite library. The mechanism matters less than the classification.
Related questions
Frontend
Medium
6 min
Frontend
Easy
6 min