Global State Management
As your app grows, you’ll need to share state across many components. Here’s how to choose the right tool.
When to Use What
| Solution | Best for | Complexity |
|---|---|---|
| useState | Local component data | None |
| Context API | Low-frequency globals (theme, auth, locale) | Low |
| Zustand | Medium apps โ minimal boilerplate | Low |
| Redux Toolkit | Large apps with complex state & devtools | Medium |
| TanStack Query | Server state (API data, caching) | Low |
Zustand โ Minimal Global Store
npm install zustand
// store/useCartStore.js
import { create } from "zustand";
const useCartStore = create((set, get) => ({
items: [],
total: 0,
addItem: (product) => set(state => {
const exists = state.items.find(i => i.id === product.id);
return {
items: exists
? state.items.map(i => i.id === product.id ? { ...i, qty: i.qty + 1 } : i)
: [...state.items, { ...product, qty: 1 }],
total: state.total + product.price,
};
}),
removeItem: (id) => set(state => ({
items: state.items.filter(i => i.id !== id),
total: state.total - (state.items.find(i => i.id === id)?.price ?? 0),
})),
clearCart: () => set({ items: [], total: 0 }),
itemCount: () => get().items.reduce((n, i) => n + i.qty, 0),
}));
// No Provider needed โ use in any component
function CartBadge() {
const count = useCartStore(s => s.items.length); // subscribe to slice
return <span className="badge">{count}</span>;
}
function AddToCartButton({ product }) {
const addItem = useCartStore(s => s.addItem);
return <button onClick={() => addItem(product)}>Add to Cart</button>;
}
Redux Toolkit (RTK) โ Enterprise Scale
npm install @reduxjs/toolkit react-redux
// store/counterSlice.js
import { createSlice } from "@reduxjs/toolkit";
const counterSlice = createSlice({
name: "counter",
initialState: { value: 0 },
reducers: {
increment: state => { state.value += 1; }, // Immer lets you "mutate" directly
decrement: state => { state.value -= 1; },
setTo: (state, action) => { state.value = action.payload; },
},
});
export const { increment, decrement, setTo } = counterSlice.actions;
export default counterSlice.reducer;
// store/index.js
import { configureStore } from "@reduxjs/toolkit";
export const store = configureStore({ reducer: { counter: counterReducer } });
// main.jsx
import { Provider } from "react-redux";
<Provider store={store}><App /></Provider>
// In a component
const count = useSelector(state => state.counter.value);
const dispatch = useDispatch();
<button onClick={() => dispatch(increment())}>+</button>
TipFor most new projects, start with useState + Context for simple globals, and add Zustand if global state gets complex. Reach for Redux Toolkit only when you need time-travel debugging or have a very large team.