State management is one of those topics where the React ecosystem has overcorrected so many times that developers newer to the space genuinely do not know where to start.
Here is the honest framework I use.
Start With Local State
Most components do not need global state. They need state that lives inside them and maybe gets passed one level down via props. useState is fine for this.
function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(c => c + 1)}>
Clicked {count} times
</button>
);
}If your component tree is not deeply nested and you are not passing state through more than two or three component levels, local state is the right choice. Do not reach for a global store preemptively.
When Props Become Painful: Context
When the same piece of state needs to be accessed by components that are far apart in the tree, prop-drilling (passing props through intermediate components that do not use them) becomes genuinely painful to maintain.
This is where React Context makes sense.
const ThemeContext = React.createContext('light');
function App() {
return (
<ThemeContext.Provider value="dark">
<DeepNestedComponent />
</ThemeContext.Provider>
);
}
function DeepNestedComponent() {
const theme = useContext(ThemeContext);
return <div className={theme}>Content</div>;
}Context works well for slowly-changing values like themes, authentication state, or user preferences. It is not great for frequently-updating state because every consumer re-renders on change.
Complex Client State: Zustand
When you need global state that updates frequently, where multiple components need to subscribe to slices of a store independently, Zustand is my current recommendation.
It is small (under 1KB), has zero boilerplate compared to Redux, and the API is so straightforward that there is almost nothing to learn.
import { create } from 'zustand';
const useStore = create((set) => ({
pads: [],
addPad: (pad) => set((state) => ({ pads: [...state.pads, pad] })),
}));When to Use Redux
Honestly? Almost never for new projects. Redux made sense when it was the only option for structured global state. Today, Zustand gives you 90% of the benefit at 10% of the complexity.
The one case where Redux (specifically Redux Toolkit) still makes sense is large team codebases where strict conventions and the DevTools ecosystem are worth the overhead.
Server State Is Different
One more thing: server state is not the same as client state. Fetched data, loading indicators, cache invalidation — these are problems that TanStack Query (React Query) solves far better than any client-side store.
If the state you are managing is data from an API, use a server state library. If it is UI-specific (modals open, selected tab, form inputs), use local state or a lightweight store.