Context
Reducers, actions with a string type, a mandatory Provider: classic Redux boilerplate shaped much of the React state-management ecosystem — including the two answers compared here, each reacting to that boilerplate in its own way. Zustand is the minimalist answer — a create() function that returns a hook, no mandatory Provider, no separate actions/reducers. RTK is the Redux team's own official answer to classic Redux's boilerplate: it keeps the architecture of pure reducers, actions, and a single immutable store, but wraps it all in createSlice(), generates the actions automatically, and uses Immer under the hood to allow direct "mutation" inside the reducer.
The comparison isn't Zustand vs. classic Redux — that would be unfair, RTK already solved much of the boilerplate that made Redux famous for its verbosity. The real question is whether it's worth keeping Redux's reducers/actions/single-store model (now with much less ceremony) or swapping it for a direct store like Zustand's.
When to choose Zustand
Zustand works well when the team wants the shortest path between "I need shared state" and "shared state working." There are no reducers, no actions with a string type, no Provider: the store is a plain hook.
import { create } from "zustand";
const useCart = create<{ items: string[]; add: (id: string) => void }>((set) => ({
items: [],
add: (id) => set((s) => ({ items: [...s.items, id] })),
}));
There's no step of "dispatching an action that a reducer intercepts" — the add function already modifies state directly via set. This reduces the number of files and the amount of indirection for small teams or for parts of the application where state is simple. The trade-off is that Zustand doesn't impose any structure: as the number of stores grows, it's up to the team to decide organizing conventions, and there's no ready-made equivalent to RTK Query for server-data caching — that's left to another library (like TanStack Query).
When to choose Redux Toolkit
RTK pays off when the project benefits from the discipline Redux has always imposed — pure reducers, a single update flow, and a traceable action history — but without paying the price of writing action switch statements by hand. createSlice() generates the actions and the reducer from an object of functions that look like they mutate state directly, thanks to the built-in Immer:
import { createSlice, configureStore } from "@reduxjs/toolkit";
const cart = createSlice({
name: "cart",
initialState: { items: [] as string[] },
reducers: {
add: (state, action: { payload: string }) => {
state.items.push(action.payload); // "mutação" segura via Immer
},
},
});
export const store = configureStore({ reducer: { cart: cart.reducer } });
That state.items.push(...) doesn't actually mutate state — Immer intercepts it and produces a new immutable object behind the scenes. RTK's real gain over Zustand shows up in two places: native Redux DevTools integration (time-travel debugging, inspecting the full action history) and RTK Query, a complete data layer for API calls with cache, automatic invalidation, and generated hooks — something Zustand doesn't try to solve. The cost is structural: RTK still requires a configureStore, a Provider wrapping the tree, and the convention of organizing state into slices by domain, which is more ceremony than Zustand's single store.
Verdict
Neither is the exotic choice here — Zustand and Redux Toolkit are each, in their own way, the mature option within the philosophy they represent; what changes is how much infrastructure the project already needs around its state. If the team wants the fewest pieces between "state" and "component," without depending on a built-in data-fetching ecosystem, Zustand delivers that with fewer new concepts to learn. If the project already is (or is going to grow into) a Redux app — needing advanced DevTools, action traceability, and an integrated server data layer — Redux Toolkit is today's recommended way to use Redux, without the classic model's boilerplate.
It's worth asking not just what the project needs today, but what it will likely need a few months from now: if a server data layer for API calls is already on the radar, adopting RTK Query from the start saves a tooling migration later. If that scenario never arrives, Zustand avoids paying upfront for complexity the project might never need to carry.