Skip to content

TopicsIntermediate

Context vs Zustand: Which Should You Use?

Compare React Context and Zustand: when Context is enough, when a store reduces re-renders, and how to choose for production apps.

Mohamed ElshahawyPublished Oct 2026
2 min read

What you'll learn

  • Context for low-frequency shared values
  • Zustand when many consumers need frequent, selective updates
  • Code shapes for both
  • A decision table and mistakes to avoid

Direct answer: use Context for values that change rarely and are read by a subtree (theme, locale, current user id). Use Zustand (or a similar store) when many unrelated components subscribe to different slices that update often.

Both can share state across the tree. They differ in how updates notify readers.

When to use Context

  • Theme, locale, auth user identity
  • Dependency injection for a feature subtree
  • Values that change on navigation or user preference, not every keystroke

When to use Zustand

  • Dashboard filters, selections, or UI chrome shared across distant screens
  • High-frequency updates where Context would re-render large subtrees
  • You want selectors so each component reads only what it needs

When not to use either

  • Isolated form inputs: local state
  • Server lists and caches: server-state library (see State Management)

Practical examples

Context (fine for rare updates):

JSX
const ThemeContext = React.createContext('light')

function App() {
  const [theme, setTheme] = React.useState('light')
  return (
    <ThemeContext.Provider value={theme}>
      <Toolbar onToggle={() => setTheme((t) => (t === 'light' ? 'dark' : 'light'))} />
      <Page />
    </ThemeContext.Provider>
  )
}

Zustand (selective subscriptions):

JavaScript
import { create } from 'zustand'

const useUi = create((set) => ({
  sidebarOpen: true,
  setSidebarOpen: (sidebarOpen) => set({ sidebarOpen }),
}))

function SidebarToggle() {
  const open = useUi((s) => s.sidebarOpen)
  const setOpen = useUi((s) => s.setSidebarOpen)
  return <button onClick={() => setOpen(!open)}>{open ? 'Close' : 'Open'}</button>
}

Comparison

NeedContextZustand
Built into ReactYesLibrary
Default update scopeAll consumers of that valueSelectors per field
Best forRare shared configFrequent shared UI/app state
RiskAccidental wide re-rendersExtra dependency and store sprawl

Common mistakes

  • One mega Context object that changes every render
  • Zustand for every tiny bit of local UI state
  • Putting server cache into Zustand without a fetch library

Checklist

  • Does the value change rarely? Prefer Context.
  • Do distant screens need frequent selective updates? Prefer a store.
  • Can local or lifted state solve it? Prefer that first.
  • Split Context by update rate if you keep Context.

Final recommendation

Start with local and lifted state. Add Context for slow-changing shared config. Reach for Zustand when Context re-renders become a measured problem or selectors clearly help.