Zustand vs Jotai vs Valtio (2026 Comparison)
All three are created by the same developer (Daishi Kato) and the pmndrs collective. They solve state management differently. Zustand uses stores. Jotai uses atoms. Valtio uses proxies. Here's which one to pick.
Quick Verdict
- Zustand — Best general-purpose store. Simple, flexible, most popular.
- Jotai — Best for granular, composable state. React-centric.
- Valtio — Best for mutable-style state. Easiest if you come from Vue/MobX.
API Comparison
Zustand — Store-Based
import { create } from 'zustand'
const useStore = create((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
}))
function Counter() {
const count = useStore((state) => state.count)
const increment = useStore((state) => state.increment)
return <button onClick={increment}>{count}</button>
}
Jotai — Atom-Based
import { atom, useAtom } from 'jotai'
const countAtom = atom(0)
const doubleAtom = atom((get) => get(countAtom) * 2) // Derived atom
function Counter() {
const [count, setCount] = useAtom(countAtom)
return <button onClick={() => setCount(c => c + 1)}>{count}</button>
}
Valtio — Proxy-Based
import { proxy, useSnapshot } from 'valtio'
const state = proxy({ count: 0 })
function Counter() {
const snap = useSnapshot(state)
return <button onClick={() => state.count++}>{snap.count}</button>
}
Key Differences
| Feature | Zustand | Jotai | Valtio |
|---|---|---|---|
| Mental model | External store | React atoms (like signals) | Mutable proxy |
| Re-render control | Selector-based | Atom-level | Automatic (proxy tracking) |
| Boilerplate | Low | Lowest | Lowest |
| Works outside React | ✅ | ❌ (React-only) | ✅ |
| DevTools | ✅ | ✅ | ✅ |
| Middleware | ✅ (persist, devtools, immer) | ✅ (atomWithStorage, etc.) | ✅ (devtools, subscribeKey) |
| Bundle size | ~1KB | ~2KB | ~3KB |
| npm downloads/week | ~4M | ~1.5M | ~500K |
Re-Rendering Behavior
This is the most important practical difference.
Zustand
Components only re-render when their selected state changes:
// Only re-renders when count changes, not when other state changes
const count = useStore((state) => state.count)
You must write selectors. Forgetting a selector causes unnecessary re-renders:
// ❌ Re-renders on ANY state change
const store = useStore()
// ✅ Re-renders only when count changes
const count = useStore((s) => s.count)
Jotai
Each atom is independently subscribed. Components only re-render when atoms they use change. No selectors needed — granularity is built in.
// Only re-renders when countAtom changes
const [count] = useAtom(countAtom)
Valtio
Proxy tracks which properties your component accesses. Only re-renders when accessed properties change. Automatic — no selectors, no atoms.
// Valtio tracks that you accessed snap.count
// Only re-renders when count changes
const snap = useSnapshot(state)
return <div>{snap.count}</div>
Winner: Jotai and Valtio for automatic granularity. Zustand requires manual selectors.
Composability
Zustand
One big store (or split into multiple). Stores are standalone:
const useAuthStore = create(...)
const useCartStore = create(...)
Jotai
Atoms compose naturally. Derived atoms depend on other atoms:
const firstNameAtom = atom('John')
const lastNameAtom = atom('Doe')
const fullNameAtom = atom((get) => `${get(firstNameAtom)} ${get(lastNameAtom)}`)
This is Jotai's superpower. Complex state derives from simple atoms.
Valtio
Derived state uses derive or computed properties:
const state = proxy({
firstName: 'John',
lastName: 'Doe',
get fullName() { return `${this.firstName} ${this.lastName}` },
})
Winner: Jotai for composable, derived state.
Async State
Zustand
Handle async in actions:
const useStore = create((set) => ({
data: null,
loading: false,
fetch: async () => {
set({ loading: true })
const data = await fetchData()
set({ data, loading: false })
},
}))
Jotai
Async atoms with Suspense:
const dataAtom = atom(async () => {
const res = await fetch('/api/data')
return res.json()
})
// Component suspends until data loads
function DataView() {
const [data] = useAtom(dataAtom)
return <div>{data.name}</div>
}
Valtio
Handle async by mutating the proxy:
const state = proxy({ data: null, loading: false })
async function fetchData() {
state.loading = true
state.data = await fetch('/api/data').then(r => r.json())
state.loading = false
}
Winner: Jotai's Suspense integration is cleanest. Zustand and Valtio are straightforward.
Persistence
Zustand
Built-in persist middleware:
const useStore = create(
persist(
(set) => ({ count: 0 }),
{ name: 'my-store' } // localStorage key
)
)
Jotai
atomWithStorage:
import { atomWithStorage } from 'jotai/utils'
const countAtom = atomWithStorage('count', 0)
Valtio
proxyWithPersist or manual:
import { proxyWithPersist } from 'valtio-yjs' // community
// Or manual: subscribe(state, () => localStorage.setItem(...))
Winner: Zustand has the most polished persist middleware.
When to Use Each
Choose Zustand When
- You want a simple, well-documented store
- State lives outside React (Node.js, vanilla JS)
- You need middleware (persist, devtools, immer)
- Team is familiar with Redux patterns (but wants less boilerplate)
- Default choice when unsure
Choose Jotai When
- State is highly composable (lots of derived values)
- You want React Suspense integration
- Fine-grained re-renders matter (large component trees)
- You think in atoms/signals
- Building forms or complex UI state
Choose Valtio When
- You prefer mutable state patterns
- Coming from Vue or MobX
- Want the least boilerplate
- Building prototypes quickly
- State mutations happen outside React (event handlers, WebSocket callbacks)
FAQ
Can I use multiple libraries together?
Yes. Use Zustand for global app state, Jotai for component-level state. They don't conflict.
Which is fastest?
All three are fast enough. Performance differences are negligible for most apps. Jotai and Valtio have slightly better automatic granularity.
Do I even need a state management library?
For simple apps, React's useState and useContext are fine. These libraries shine when state is shared across many components or needs persistence/middleware.
What about Redux?
Redux Toolkit is still used in enterprise. But for new projects, Zustand covers the same patterns with 10x less boilerplate.
What about React 19 and Server Components?
All three work with client components. Server components don't need client-side state management. Use these for interactive client-side state.
Bottom Line
Zustand is the default choice for most React apps in 2026. Simple, proven, great ecosystem. Jotai is better when your state is highly composable with lots of derived values. Valtio is best when you want to just mutate objects and have the UI update.
All three are excellent. You can't go wrong. Pick based on mental model preference and move on.