State management shouldn’t be a part-time job. Yet every frontend engineer I’ve met in recent years has had to deal with it. While the pace of new state management libraries has slowed, there are still plenty to choose from. After trying almost all of them, I’ve found Zustand to be the best fit for most teams.
I used Redux for years. It works. But in 2025, do you really need actions, reducers, dispatchers, and a boilerplate tax just to update a loading spinner?
Here's why Zustand wins:
- No File Gymnastics
// Zustand (single file)
import { create } from 'zustand'
const useUserStore = create<StoreState>((set) => ({
user: null,
setUser: (user) => set({ user }),
}))
// Redux Toolkit equivalent (minimum 2 files):
// - store/userSlice.ts (actions/reducers)
// - store/store.ts (root reducer configuration)
Fact: Zustand typically requires ~50% less code than Redux Toolkit for basic to medium-complexity state management. (comparison)
- Syntax That Doesn’t Fight TypeScript
// Zustand (inferred types)
const useStore = create((set) => ({
user: null as User | null,
setUser: (user: User) => set({ user }),
}))
// Redux Toolkit equivalent (type-safe but verbose)
interface UserState {
user: User | null
}
const userSlice = createSlice({
name: 'user',
initialState: { user: null } as UserState, // Fix: Matches interface
reducers: {
setUser: (state, action: PayloadAction<User>) => {
state.user = action.payload
},
},
})
Fact: While both support TypeScript, Zustand’s mutable patterns align better with modern React than Redux’s immutable reducers.
- Data Fetching With Simplicity
// Zustand + TanStack Query
import { useQuery } from '@tanstack/react-query'
const useUserData = () => {
return useQuery({
queryKey: ['user'],
queryFn: fetchUser, // No dispatch, no actions
})
}
// Redux equivalent requires:
// - Thunks/sagas for async logic
// - Reducers to store data
// - Selectors to retrieve it
Fact: It’s simpler to fetch data with Tanstack Query than with Redux Toolkit. Trust me on this.
Why This Matters
- Fewer abstractions: Zustand + TanStack Query mirrors how React itself works
- Guardrails where they matter: Redux Toolkit still shines for complex state transitions (e.g., undo/redo)
- No more "action type" bugs: Zustand’s API surfaces errors at write-time, not runtime
When Redux Still Makes Sense
- Large teams where strict patterns prevent chaos
- Apps requiring time-travel debugging via Redux DevTools (one word)
- Legacy codebases where migration cost > benefit
Bottom line
Zustand + TanStack Query lets us ship features faster by focusing on what the app does instead of how state moves. Less code, fewer bugs, and zero existential debates about where to put a loading spinner
Hot take? Maybe. But I’d rather debug business logic than action type typos.