Do You Even Need Redux in 2026? A Practical Decision Guide
3 min read
I haven't reached for Redux on a new project in a while — and I don't think Redux is bad. Both things are true, and the tension between them is exactly what this post untangles. State management in React stopped being a single decision ("which library?") and became a diagnosis: what kind of state do you actually have? Get that right and the library picks itself.
The diagnosis starts with a split most Redux-era codebases missed: server state (data fetched from APIs — caching, refetching, staleness) and client state (UI truth that lives only in the browser — filters, modals, form progress). A huge share of what teams stuffed into Redux stores was server state, and dedicated tools (TanStack Query, SWR) now handle it better than any store ever did. Adopt one, and the client state that remains is usually small — which reframes everything below.
When Context + useReducer is genuinely enough
For state that a subtree needs and that changes together — theme, session, a wizard's progress — React's built-ins are not a compromise; they're the right size:
type CartAction = { type: 'add'; item: Item } | { type: 'remove'; id: string };
function cartReducer(state: Item[], action: CartAction): Item[] {
switch (action.type) {
case 'add':
return [...state, action.item];
case 'remove':
return state.filter((i) => i.id !== action.id);
}
}
const [cart, dispatch] = useReducer(cartReducer, []);
dispatch({ type: 'add', item });Notice this is the Redux pattern — actions, a pure reducer, predictable transitions — without the store, the middleware, or the dependency. Pair the action type with a discriminated union and you keep most of Redux's rigor for free. The known limit: Context re-renders every consumer on every change, so it suits state that changes occasionally, not sixty times a second.
When Zustand (or similar) fits
The moment state is needed across the app — many unrelated components reading different slices, updating frequently — Context's re-render behavior and provider nesting start to hurt. That's the niche where a light store shines:
import { create } from 'zustand';
const useCart = create((set) => ({
items: [],
add: (item) => set((s) => ({ items: [...s.items, item] })),
remove: (id) => set((s) => ({ items: s.items.filter((i) => i.id !== id) })),
}));
// Components subscribe to exactly the slice they use:
const count = useCart((s) => s.items.length); // re-renders only when count changesSelective subscriptions, no providers, a few kilobytes, near-zero ceremony. For small-to-mid product teams — including the healthcare product I lead — a query library for server state plus Zustand for the modest client state that remains covers essentially everything, and new developers read it fluently in an afternoon.
When Redux still earns its place
Redux Toolkit remains the right call when the organizational problems it solves are your actual problems:
- Large apps with complex, interacting client state — an editor, a trading UI, a design tool — where state transitions genuinely benefit from strict, centralized, auditable flow.
- Big or rotating teams that need one enforced pattern instead of per-developer conventions. Redux's rigidity is a governance feature; it's the Angular argument applied to state.
- Serious devtools needs — time-travel debugging and action logs are still unmatched for diagnosing "how did the state get like this?" in tangled flows.
- It's already there and working. Migrating a healthy Redux codebase to be fashionable is negative-value work. Don't.
The same cart in Redux Toolkit, for honest comparison: define a slice with
createSlice({ name, initialState, reducers: { add, remove } }), register it in a
store, wrap the app in a <Provider>, then useSelector/useDispatch in components.
Perfectly reasonable — and visibly more machinery than the ten-line Zustand version.
That overhead buys structure. The question is only whether you need the structure.
The real question isn't the library
It's an inventory. What's server state? (Query library — most of your "state problem"
dissolves here.) What's local? (useState, and keep it local.) What's shared and
occasional? (Context/useReducer.) What's shared, frequent, cross-cutting? (Zustand — or
Redux when scale, team size, or auditability demand its discipline.) Answer those four
honestly and you'll notice the library debate mostly evaporates — which is the quiet
truth of state management in 2026: Redux didn't lose; the problem got factored into
pieces, and Redux's piece got smaller.