Zustand Alternative
Overview
While Redux Toolkit vastly improved legacy Redux, it still requires a significant amount of boilerplate (creating a store file, wrapping the app in a <Provider>, creating slices).
In recent years, the React community has heavily shifted towards a lightweight, unopinionated global state manager called Zustand (German for 'State').
Zustand provides the granular selector performance of Redux (preventing useless re-renders) but with zero boilerplate. There are no Providers, no Context wrappers, no complex Dispatchers, and no Slices. You simply define a custom hook that holds your state and actions, and any component anywhere in your app can immediately import and use it.
For 95% of modern applications, Zustand offers the perfect balance: the simplicity of the Context API combined with the high-performance architecture of Redux.
Syntax
import { create } from 'zustand';
// 1. Create the global store (Notice: No Providers needed!)
export const useBearStore = create((set) => ({
bears: 0,
// The 'set' function merges updates directly into the state
increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
removeAllBears: () => set({ bears: 0 }),
}));
// 2. Use it instantly in any component
function BearCounter() {
// Using a selector to ONLY subscribe to the 'bears' number
const bears = useBearStore((state) => state.bears);
return <h1>{bears} around here ...</h1>;
}
function Controls() {
// Subscribing to the action function
const increasePopulation = useBearStore((state) => state.increasePopulation);
return <button onClick={increasePopulation}>Add Bear</button>;
}Common Pitfalls
- Failing to use Selectors: If you write
const store = useBearStore(), you are subscribing to the ENTIRE store. If ANY property in the store changes, this component will re-render. Always use selector functions to grab specific properties:const bears = useBearStore(state => state.bears).
Interview Questions
Context API suffers from performance issues due to full-tree re-renders on every change. Redux solves the performance issue but introduces massive boilerplate and structural complexity. Zustand solves both: it prevents unnecessary re-renders using selectors (like Redux), but requires virtually zero setup boilerplate and completely eliminates the need for Provider wrappers (unlike Context).
Yes, effortlessly. Unlike Redux (which requires middleware like Thunk or Saga to handle async actions), in Zustand, your actions are just standard JavaScript functions. You can make them async, await an API call, and then call the set function whenever the data arrives.
Real-World Example
Async Data Fetching Store: This demonstrates how remarkably clean global logic can be. In Redux, handling an async login requires creating an async thunk, building a slice, and adding 3 extra reducers for pending, fulfilled, and rejected states. Zustand achieves it in a single readable block.
import { create } from 'zustand';
const useAuthStore = create((set) => ({
user: null,
isLoading: false,
// Async actions are natively supported. No middleware needed!
login: async (email, password) => {
set({ isLoading: true });
try {
const response = await api.authenticate(email, password);
set({ user: response.data, isLoading: false });
} catch (error) {
set({ isLoading: false });
alert("Login failed");
}
},
logout: () => set({ user: null })
}));Check Your Knowledge
Test your understanding of Zustand Alternative with these quick questions.