Frontend State Management: When to Use What

The state management landscape has consolidated significantly. The “Redux for everything” era is over; modern apps use specialized tools per state category. Senior frontend interviews probe whether you understand the categories and the right tool for each.

The five categories of state

  1. Server state: data fetched from APIs (lists, items, user info)
  2. Client state: UI state (selected tab, expanded item)
  3. Form state: what the user is currently typing/selecting
  4. URL state: route, query params, hash
  5. Computed state: derived from other state

Different tools for different categories. Mixing them causes pain.

Server state: TanStack Query / SWR / Apollo

Built specifically for the lifecycle of data fetched from servers:

  • Caching with TTLs
  • Background refetching
  • Stale-while-revalidate
  • Mutations with optimistic updates
  • Pagination, infinite scroll
  • Retry logic

TanStack Query (formerly React Query) is the dominant choice in 2026. SWR is simpler. Apollo is for GraphQL specifically.

Client state: Zustand / Jotai / Redux Toolkit

For state that is yours, not the server’s:

Zustand

Tiny (~3KB), simple. Single store with hooks. The default for small-to-medium apps.

const useStore = create((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 }))
}));

Jotai

Atomic state. Each piece of state is an atom. Components subscribe to specific atoms; only re-render when those change.

Best for fine-grained state with many disjoint slices.

Redux Toolkit (RTK)

Modern Redux. Less boilerplate than classic Redux. Best for large apps with complex state interactions, time-travel debugging needs, or strict structural patterns.

Adoption is declining for new projects but RTK remains in many large codebases.

Form state: React Hook Form / Formik

Forms have unique requirements (validation, dirty tracking, submission). General state libraries do not handle them well.

React Hook Form is the modern default. Formik still in use; new projects pick RHF.

URL state: nuqs / route params

Search params (filters, page numbers, selected items) often belong in the URL — sharable, browser-back-friendly, indexable.

nuqs (or similar) provides typed query-param management without manual parsing/encoding.

The Redux trap

Older codebases store everything in Redux: server data, form state, UI state. This produces:

  • Boilerplate without benefit
  • Re-renders on every state change
  • Confused mental model — what kind of state is this?

Modern best practice: server state in TanStack Query, client state in Zustand or Context, form state in RHF, URL state in route library. Redux only where genuinely needed.

Context: when and when not

React’s built-in useContext is fine for:

  • Theme
  • Locale
  • Authentication state (current user)

Not great for:

  • Frequently updated state — every consumer re-renders on every change
  • Complex selectors
  • State that needs persistence

Selectors and memoization

For derived state from a store, use selectors:

const visibleTodos = useStore(s => s.todos.filter(t => !t.done));

Without memoization, the component re-renders on every store change, even unrelated ones. Most modern stores (Zustand, Redux Toolkit) handle selector memoization for you.

State that survives reload

Persistence options:

  • localStorage / sessionStorage — for non-sensitive client state
  • IndexedDB — for larger datasets
  • Server — for anything that should survive across devices

Zustand has a persist middleware. Redux has redux-persist. Both reasonably reliable for the common cases.

Common mistakes

  • Using Redux for server data instead of TanStack Query
  • useState + useEffect to fetch data (manual implementation of what libraries handle)
  • Putting too much in Context (re-render storm)
  • Storing form state at the global level (should be local)
  • Storing URL state somewhere other than the URL (loses shareability)

Frequently Asked Questions

What about Recoil?

Effectively dead. Replaced by Jotai for atomic state needs.

Should I use Server Components instead of state management?

For server-rendered content, yes — RSC reduces client-side state needs significantly. Interactive parts still need client state.

How do I structure a Zustand store for a large app?

Slice the store into independent stores by domain (user, cart, ui). Compose where needed. Keep each store small.

Scroll to Top