React
3 minutes readState management compared: Redux Toolkit vs Zustand vs Jotai
Three different mental models for the same problem, a working example of each, a side-by-side comparison table, and a direct answer to "which one should I actually use?"

“Which state manager should I use?” is really three separate questions hiding under one umbrella: how much boilerplate am I willing to write, do I need time-travel debugging, and is my state truly global or just shared between a few nearby components? Redux Toolkit, Zustand and Jotai answer those questions very differently.
Redux Toolkit — one central store, everything explicit
Redux Toolkit (RTK) is the modern, opinionated way to write Redux: less boilerplate than classic Redux, but the same core idea — a single store, actions that describe what happened, and reducers that decide how state changes.
import { createSlice } from "@reduxjs/toolkit";
const counterSlice = createSlice({
name: "counter",
initialState: { value: 0 },
reducers: {
increment: (state) => { state.value += 1; }, // Immer runs under the hood — this "mutation" is safe
incrementBy: (state, action: { payload: number }) => { state.value += action.payload; },
},
});
export const { increment, incrementBy } = counterSlice.actions;
export default counterSlice.reducer;
// inside a component
const count = useSelector((state: RootState) => state.counter.value);
const dispatch = useDispatch();
dispatch(increment());Zustand — a store that’s just a hook
Zustand drops the ceremony. A store is just a function; components subscribe only to the slice they read, so re-renders stay minimal without extra memoization.
import { create } from "zustand";
const useCounterStore = create<{ value: number; increment: () => void }>((set) => ({
value: 0,
increment: () => set((s) => ({ value: s.value + 1 })),
}));
// inside a component — only re-renders when value changes
const count = useCounterStore((s) => s.value);
const increment = useCounterStore((s) => s.increment);Jotai — state as atoms, not one big object
Jotai flips the model: instead of one store holding everything, state lives in small independent atoms that compose together. No selectors are needed — a component reading an atom re-renders only when that atom changes.
import { atom, useAtom } from "jotai";
const countAtom = atom(0);
const doubledAtom = atom((get) => get(countAtom) * 2); // derived atom, recomputed automatically
// inside a component
const [count, setCount] = useAtom(countAtom);
const [doubled] = useAtom(doubledAtom);Side-by-side comparison
So which one, really?
- Pick Redux Toolkit when the state is genuinely cross-cutting, the team is bigger than two or three people, and you also want RTK Query’s caching for server data.
- Pick Zustand when you want global state without ceremony — most personal projects and mid-size apps land here.
- Pick Jotai when your state is naturally a graph of small, derived values — multi-step forms, filter panels, anywhere fields depend on each other.
- And before reaching for any of them: check whether the state is truly global, or just a prop/context passed down a few levels — not everything needs a store.
The best state manager is the smallest one that makes your current problem easy to reason about. It is perfectly fine — even good — to use different tools for different parts of one app.