Updated June 2026. Tested on React 19 and modern JavaScript. Part of the Techalyst React series.
State management in React gets talked about as if you need a big library on day one. You usually do not. Most state is local and stays that way. The question only becomes interesting when state has to be shared across many unrelated parts of the app, and even then there is a progression. Picking the right rung saves you from both a tangle and an over-built setup.
Start local, then lift, then context
Before any library: state used by one component lives there with useState. State shared by a few nearby components gets lifted to a common parent. State shared more widely, app-wide and slow-changing, fits Context, often paired with useReducer as a small built-in store. For a lot of apps, that is the whole answer, and you never install anything.
Context's limit is that any change re-renders every consumer, so it struggles with state that updates frequently or is large. That is the point where a real store earns its place.
Zustand: the small global store
Zustand is a tiny library that gives you a global store as a hook, with no provider to wrap and almost no boilerplate:
import { create } from 'zustand';
const useStore = create((set) => ({
count: 0,
increment: () => set((s) => ({ count: s.count + 1 })),
}));
function Counter() {
const count = useStore((s) => s.count); // subscribe to just this slice
const increment = useStore((s) => s.increment);
return <button onClick={increment}>{count}</button>;
}
The selector, s => s.count, is the important bit: a component only re-renders when the slice it selected changes, which is exactly what Context cannot do. For most apps that have outgrown Context, Zustand is the pragmatic choice.
Redux Toolkit: the structured option
Redux Toolkit is the modern, official way to use Redux, and it is the standard for large apps and teams. It is more structured: you define slices of state with their reducers, combine them into a store, and get excellent devtools, middleware, and a huge ecosystem:
import { createSlice, configureStore } from '@reduxjs/toolkit';
const counterSlice = createSlice({
name: 'counter',
initialState: { value: 0 },
reducers: {
increment: (state) => { state.value += 1; }, // Immer lets this look like a mutation
},
});
const store = configureStore({ reducer: { counter: counterSlice.reducer } });
There is more ceremony than Zustand, but in return you get strong conventions, time-travel debugging, and predictability that pays off on a big codebase with many contributors. The "mutation" in the reducer is safe because Toolkit uses Immer under the hood.
The rule everyone misses
Here is the most useful thing to know. A lot of what people throw into Redux is not really application state, it is server data, and that belongs in a data-fetching library, not a global store. Caching, refetching, and loading states for API data are solved by TanStack Query. Once you move server data there, the amount of true global state left is usually small, often small enough for Context or Zustand. Reaching for Redux to hold your API responses is the classic over-engineering of React state.
How to choose
Keep it simple. Server data goes to a query library. Small, app-wide UI state goes to Context with useReducer, or Zustand when you want selective re-renders without boilerplate. Reach for Redux Toolkit when the app is large, the team is several people, and you want strict structure and first-class devtools. And do not promote local state to a global store just because a tutorial used one.
Wrapping up
React state management is a ladder, not a single tool. Most state is local, some is lifted, app-wide slow-changing state fits Context with useReducer, and when that re-renders too much you move to Zustand for a light store with selective subscriptions or Redux Toolkit for a large, structured one. The biggest win is recognising that server data is not global UI state and belongs in a query library, which usually shrinks your global state to something small. Match the tool to how the state actually behaves and you avoid both extremes.
All comments ()
No comments yet
Be the first to leave a comment on this post.