Zustand Examples
Explore these practical examples to learn how to use Zustand in real-world applications.
Simple Counter
A basic counter example demonstrating state updates and actions.
import { create } from 'zustand'
const useCounterStore = create((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
reset: () => set({ count: 0 }),
}))
function Counter() {
const { count, increment, decrement, reset } = useCounterStore()
return (
<div>
<h1>{count}</h1>
<button onClick={increment}>+1</button>
<button onClick={decrement}>-1</button>
<button onClick={reset}>Reset</button>
</div>
)
}Shopping Cart
A shopping cart with add, remove, and quantity management.
import { create } from 'zustand'
interface CartItem {
id: string
name: string
price: number
quantity: number
}
const useCartStore = create((set) => ({
items: [] as CartItem[],
addItem: (item: Omit<CartItem, 'quantity'>) =>
set((state) => {
const existing = state.items.find((i) => i.id === item.id)
if (existing) {
return {
items: state.items.map((i) =>
i.id === item.id ? { ...i, quantity: i.quantity + 1 } : i
),
}
}
return { items: [...state.items, { ...item, quantity: 1 }] }
}),
removeItem: (id: string) =>
set((state) => ({
items: state.items.filter((item) => item.id !== id),
})),
updateQuantity: (id: string, quantity: number) =>
set((state) => ({
items: state.items.map((item) =>
item.id === id ? { ...item, quantity } : item
),
})),
clearCart: () => set({ items: [] }),
total: () => {
const state = useCartStore.getState()
return state.items.reduce(
(sum, item) => sum + item.price * item.quantity,
0
)
},
}))Form Management
Managing form state with validation and submission.
import { create } from 'zustand'
interface FormState {
values: Record<string, any>
errors: Record<string, string>
isSubmitting: boolean
}
const useFormStore = create<FormState & {
setField: (name: string, value: any) => void
setError: (name: string, error: string) => void
reset: () => void
submit: () => Promise<void>
}>((set, get) => ({
values: {},
errors: {},
isSubmitting: false,
setField: (name, value) =>
set((state) => ({
values: { ...state.values, [name]: value },
errors: { ...state.errors, [name]: '' },
})),
setError: (name, error) =>
set((state) => ({
errors: { ...state.errors, [name]: error },
})),
reset: () =>
set({ values: {}, errors: {}, isSubmitting: false }),
submit: async () => {
set({ isSubmitting: true })
try {
const { values } = get()
// Submit form data
await fetch('/api/submit', {
method: 'POST',
body: JSON.stringify(values),
})
set({ isSubmitting: false })
} catch (error) {
set({ isSubmitting: false })
}
},
}))Async Data Fetching
Handling API calls with loading and error states.
import { create } from 'zustand'
interface User {
id: number
name: string
email: string
}
interface UserStore {
users: User[]
loading: boolean
error: string | null
fetchUsers: () => Promise<void>
}
const useUserStore = create<UserStore>((set) => ({
users: [],
loading: false,
error: null,
fetchUsers: async () => {
set({ loading: true, error: null })
try {
const response = await fetch('https://api.example.com/users')
if (!response.ok) throw new Error('Failed to fetch')
const users = await response.json()
set({ users, loading: false })
} catch (error) {
set({
error: error instanceof Error ? error.message : 'Unknown error',
loading: false,
})
}
},
}))
function UserList() {
const { users, loading, error, fetchUsers } = useUserStore()
useEffect(() => {
fetchUsers()
}, [])
if (loading) return <div>Loading...</div>
if (error) return <div>Error: {error}</div>
return (
<ul>
{users.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
)
}