About Zustand
Zustand is a small, fast and scalable bearbones state-management solution using simplified flux principles. Has a comfy API based on hooks, isn't boilerplatey or opinionated.
Zustand vs Recoil vs Redux
Zustand vs Recoil
Zustand | Recoil |
Zustand depends on atom object referential identities. | Recoil depends on atom string keys |
Zustand doesn’t require you to wrap your app in a context provider.
Zustand store is an external store, making it more suitable when access outside of React is required. | Recoil needs to wrap your app in a context provider. |
With Zustand, it is recommended that the user manually apply render optimizations by using selectors. | Recoil makes render optimizations through atom dependency. |
import { atom, useRecoilState } from 'recoil' const countAtom = atom({ key: 'count', default: 0, }) const Component = () => { const [count, setCount] = useRecoilState(countAtom) // ... }
import { create } from 'zustand' type State = { count: number setCount: (countCallback: (count: number)) => void } const useCountStore = create<State>((set) => ({ count: 0, setCount: (countCallback) => set((state) => ({ count: countCallback(state.count) })), })) const Component = () => { const { count, setCount } = useCountStore() // ... }
Zustand vs Redux (react-redux)
Conceptually similar - both are based on an immutable state model.
Zustand | Redux |
Lightweight | More feature-rich |
Zustand doesn’t require you to wrap your app in a context provider.
Zustand store is more suitable when access outside of React is required. | Redux requires your app to be wrapped in context providers |
Render optimization similar to redux → it is recommended that you manually apply render optimizations by using selectors. | Render optimization similar to zustand → it is recommended that you manually apply render optimizations by using selectors. |
ㅤ | ㅤ |
import { create } from 'zustand' type State = { count: number } type Actions = { increment: (qty: number) => void decrement: (qty: number) => void } const useCountStore = create<State & Actions>((set) => ({ count: 0, increment: (qty: number) => set((state) => ({ count: state.count + qty })), decrement: (qty: number) => set((state) => ({ count: state.count - qty })), }))
import { createStore } from 'redux' import { useSelector, useDispatch } from 'react-redux' type State = { count: number type: 'increment' | 'decrement' qty: number } const countReducer = (state: State) => { switch (action.type) { case 'increment': return { count: state.count + action.qty } case 'decrement': return { count: state.count - action.qty } default: return state } } const countStore = createStore(countReducer)
We’re using Zustand
For our client-state management library, we’re using Zustand to store synchronous data globally + TanStack Query v3 (which is a server-state library) for managing asynchronous operations between the server and client.
Why we like Zustand:
- Straightforward
- Less code
- Straightforward documentation
- Readable code