Skip to main content 首页 创作者 bobmatnyc claude-mpm-skills zustand
zustand Minimal, unopinionated state management library for React with simple hook-based API, no providers, and minimal boilerplate for global state without Redux complexity.
跳到安装 Skills Marketplace 发现并探索由社区构建的 Agent Skills
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/bobmatnyc/claude-mpm-skills --skill zustand命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
下载 Zip 下载中... name zustand description Minimal, unopinionated state management library for React with simple hook-based API, no providers, and minimal boilerplate for global state without Redux complexity. user-invocable false disable-model-invocation true progressive_disclosure {"entry_point":["summary","when_to_use","quick_start"],"full_content":true,"token_estimates":{"entry":70,"full":4500}}
Zustand State Management
Summary
Zustand is a minimal, unopinionated state management library for React. No providers, no boilerplate—just a simple hook-based API that feels natural in React applications.
When to Use
React apps needing global state without Redux complexity
Projects wanting minimal boilerplate and bundle size
Teams preferring direct state mutations over reducers
SSR applications (Next.js) requiring flexible state hydration
Migrating from Redux/Context API to simpler solution
Quick Start
npm install zustand
import { create }
{
:
:
:
}
useCounterStore = create< >( ({
: ,
: ( ({ : state. + })),
: ( ({ : state. - })),
}))
{ useCounterStore }
( ) {
{ count, increment, decrement } = ()
(
)
}
from
'zustand'
interface
CounterState
count
number
increment
() =>
void
decrement
() =>
void
export
const
CounterState
(set ) =>
count
0
increment
() =>
set
(state ) =>
count
count
1
decrement
() =>
set
(state ) =>
count
count
1
import
from
'@/stores/useCounterStore'
export
function
Counter
const
useCounterStore
return
<div >
<p > Count: {count}</p >
<button onClick ={increment} > +</button >
<button onClick ={decrement} > -</button >
</div >
Complete Zustand Guide
Core Concepts
Store Creation import { create } from 'zustand'
interface BearState {
bears : number
addBear : () => void
}
const useBearStore = create<BearState >((set ) => ({
bears : 0 ,
addBear : () => set ((state ) => ({ bears : state.bears + 1 })),
}))
const useStore = create<State >((set, get ) => ({
count : 0 ,
increment : () => {
const currentCount = get ().count
set ({ count : currentCount + 1 })
},
}))
State Access Patterns
const state = useStore ()
const bears = useStore ((state ) => state.bears )
const addBear = useStore ((state ) => state.addBear )
const { bears, addBear } = useStore ((state ) => ({
bears : state.bears ,
addBear : state.addBear ,
}))
const bears = useStore ((state ) => state.bears )
const fish = useStore ((state ) => state.fish )
Mutations interface TodoState {
todos : Todo []
addTodo : (text : string ) => void
toggleTodo : (id : string ) => void
removeTodo : (id : string ) => void
}
const useTodoStore = create<TodoState >((set ) => ({
todos : [],
addTodo : (text ) => set ((state ) => ({
todos : [...state.todos , { id : nanoid (), text, completed : false }]
})),
toggleTodo : (id ) => set ((state ) => ({
todos : state.todos .map (todo =>
todo.id === id ? { ...todo, completed : !todo.completed } : todo
)
})),
removeTodo : (id ) => set ((state ) => ({
todos : state.todos .filter (todo => todo.id !== id)
})),
}))
React Integration
useStore Hook function BearCounter ( ) {
const bears = useBearStore ((state ) => state.bears )
return <h1 > {bears} bears around here...</h1 >
}
function Controls ( ) {
const addBear = useBearStore ((state ) => state.addBear )
return <button onClick ={addBear} > Add bear</button >
}
Shallow Comparison import { shallow } from 'zustand/shallow'
const { nuts, honey } = useBearStore (
(state ) => ({ nuts : state.nuts , honey : state.honey }),
shallow
)
const treats = useBearStore (
(state ) => state.treats ,
(prev, next ) => prev.length === next.length
)
Outside React Components
const count = useStore.getState ().count
const unsubscribe = useStore.subscribe (
(state ) => console .log ('Count changed:' , state.count )
)
useStore.setState ({ count : 42 })
useStore.setState ((state ) => ({ count : state.count + 1 }))
TypeScript Patterns
Typed Store Creation interface UserState {
user : User | null
setUser : (user : User ) => void
clearUser : () => void
}
const useUserStore = create<UserState >((set ) => ({
user : null ,
setUser : (user ) => set ({ user }),
clearUser : () => set ({ user : null }),
}))
const user = useUserStore ((state ) => state.user )
Store Type Inference
type UserStoreState = ReturnType <typeof useUserStore.getState >
type Selector <T> = (state : UserState ) => T
const selectUsername : Selector <string | undefined > = (state ) =>
state.user ?.name
Combining Multiple Stores
function useHybridStore<T, U>(
selector1 : (state : State1 ) => T,
selector2 : (state : State2 ) => U
): [T, U] {
return [
useStore1 (selector1),
useStore2 (selector2),
]
}
const [user, theme] = useHybridStore (
(s ) => s.user ,
(s ) => s.theme
)
Slices Pattern
Creating Slices
export interface AuthSlice {
user : User | null
login : (credentials : Credentials ) => Promise <void >
logout : () => void
}
export const createAuthSlice : StateCreator <
AuthSlice & TodoSlice ,
[],
[],
AuthSlice
> = (set ) => ({
user : null ,
login : async (credentials) => {
const user = await api.login (credentials)
set ({ user })
},
logout : () => set ({ user : null }),
})
export interface TodoSlice {
todos : Todo []
addTodo : (text : string ) => void
}
export const createTodoSlice : StateCreator <
AuthSlice & TodoSlice ,
[],
[],
TodoSlice
> = (set ) => ({
todos : [],
addTodo : (text ) => set ((state ) => ({
todos : [...state.todos , { id : nanoid (), text, completed : false }]
})),
})
import { create } from 'zustand'
import { createAuthSlice, AuthSlice } from './authSlice'
import { createTodoSlice, TodoSlice } from './todoSlice'
export const useStore = create<AuthSlice & TodoSlice >()((...a ) => ({
...createAuthSlice (...a),
...createTodoSlice (...a),
}))
Cross-Slice Communication export const createTodoSlice : StateCreator <
AuthSlice & TodoSlice ,
[],
[],
TodoSlice
> = (set, get ) => ({
todos : [],
addTodo : (text ) => {
const user = get ().user
if (!user) throw new Error ('Not authenticated' )
set ((state ) => ({
todos : [...state.todos , {
id : nanoid (),
text,
userId : user.id ,
completed : false
}]
}))
},
})
Middleware
Persist Middleware import { create } from 'zustand'
import { persist, createJSONStorage } from 'zustand/middleware'
interface PreferencesState {
theme : 'light' | 'dark'
language : string
setTheme : (theme : 'light' | 'dark' ) => void
}
export const usePreferencesStore = create<PreferencesState >()(
persist (
(set ) => ({
theme : 'light' ,
language : 'en' ,
setTheme : (theme ) => set ({ theme }),
}),
{
name : 'preferences-storage' ,
storage : createJSONStorage (() => localStorage ),
partialize : (state ) => ({ theme : state.theme }),
version : 1 ,
migrate : (persistedState : any , version : number ) => {
if (version === 0 ) {
persistedState.language = 'en'
}
return persistedState as PreferencesState
},
}
)
)
const customStorage = {
getItem : async (name : string ) => {
const value = await AsyncStorage .getItem (name)
return value ?? null
},
setItem : async (name : string , value : string ) => {
await AsyncStorage .setItem (name, value)
},
removeItem : async (name : string ) => {
await AsyncStorage .removeItem (name)
},
}
const useStore = create (
persist (
(set ) => ({ }),
{
name : 'app-storage' ,
storage : createJSONStorage (() => customStorage)
}
)
)
DevTools Middleware import { devtools } from 'zustand/middleware'
interface CounterState {
count : number
increment : () => void
}
const useCounterStore = create<CounterState >()(
devtools (
(set ) => ({
count : 0 ,
increment : () => set ((state ) => ({ count : state.count + 1 }), false , 'increment' ),
}),
{
name : 'CounterStore' ,
enabled : process.env .NODE_ENV === 'development'
}
)
)
set ({ count : 42 }, false , 'setCount' )
set ((state ) => ({ count : state.count + 1 }), false , { type : 'increment' , amount : 1 })
Immer Middleware import { immer } from 'zustand/middleware/immer'
interface TodoState {
todos : Todo []
addTodo : (text : string ) => void
toggleTodo : (id : string ) => void
}
const useTodoStore = create<TodoState >()(
immer ((set ) => ({
todos : [],
addTodo : (text ) => set ((state ) => {
state.todos .push ({ id : nanoid (), text, completed : false })
}),
toggleTodo : (id ) => set ((state ) => {
const todo = state.todos .find (t => t.id === id)
if (todo) todo.completed = !todo.completed
}),
}))
)
Combining Middleware const useStore = create<State >()(
devtools (
persist (
immer ((set ) => ({
})),
{ name : 'app-storage' }
),
{ name : 'AppStore' }
)
)
Async Actions & API Integration
Basic Async Actions interface UserState {
users : User []
loading : boolean
error : string | null
fetchUsers : () => Promise <void >
}
const useUserStore = create<UserState >((set ) => ({
users : [],
loading : false ,
error : null ,
fetchUsers : async () => {
set ({ loading : true , error : null })
try {
const users = await api.getUsers ()
set ({ users, loading : false })
} catch (error) {
set ({ error : error.message , loading : false })
}
},
}))
Optimistic Updates interface TodoState {
todos : Todo []
addTodo : (text : string ) => Promise <void >
}
const useTodoStore = create<TodoState >((set, get ) => ({
todos : [],
addTodo : async (text) => {
const tempId = `temp-${Date .now()} `
const optimisticTodo = { id : tempId, text, completed : false }
set ((state ) => ({ todos : [...state.todos , optimisticTodo] }))
try {
const savedTodo = await api.createTodo (text)
set ((state ) => ({
todos : state.todos .map (t =>
t.id === tempId ? savedTodo : t
)
}))
} catch (error) {
set ((state ) => ({
todos : state.todos .filter (t => t.id !== tempId)
}))
throw error
}
},
}))
Request Deduplication interface DataState {
data : Data | null
loading : boolean
fetchData : () => Promise <void >
}
let currentRequest : Promise <void > | null = null
const useDataStore = create<DataState >((set ) => ({
data : null ,
loading : false ,
fetchData : async () => {
if (currentRequest) return currentRequest
set ({ loading : true })
currentRequest = api.getData ()
.then ((data ) => {
set ({ data, loading : false })
})
.catch ((error ) => {
set ({ loading : false })
throw error
})
.finally (() => {
currentRequest = null
})
return currentRequest
},
}))
Computed Values (Selectors)
Basic Selectors interface TodoState {
todos : Todo []
}
const selectCompletedCount = (state : TodoState ) =>
state.todos .filter (t => t.completed ).length
const selectActiveCount = (state : TodoState ) =>
state.todos .filter (t => !t.completed ).length
function TodoStats ( ) {
const completedCount = useTodoStore (selectCompletedCount)
const activeCount = useTodoStore (selectActiveCount)
return <div > {completedCount} / {activeCount + completedCount}</div >
}
Derived State in Store interface TodoState {
todos : Todo []
get completed (): Todo []
get active (): Todo []
get stats (): { total : number ; completed : number ; active : number }
}
const useTodoStore = create<TodoState >((set, get ) => ({
todos : [],
get completed () {
return get ().todos .filter (t => t.completed )
},
get active () {
return get ().todos .filter (t => !t.completed )
},
get stats () {
const todos = get ().todos
return {
total : todos.length ,
completed : todos.filter (t => t.completed ).length ,
active : todos.filter (t => !t.completed ).length ,
}
},
}))
const stats = useTodoStore ((state ) => state.stats )
Parameterized Selectors
const selectTodoById = (id : string ) => (state : TodoState ) =>
state.todos .find (t => t.id === id)
function TodoItem ({ id }: { id: string } ) {
const todo = useTodoStore (selectTodoById (id))
return <div > {todo?.text}</div >
}
Performance Optimization
Subscription Patterns
useEffect (() => {
const unsubscribe = useTodoStore.subscribe (
(state ) => state.todos ,
(todos ) => {
console .log ('Todos changed:' , todos)
}
)
return unsubscribe
}, [])
const unsubscribe = useTodoStore.subscribe (
(state ) => state.todos .length ,
(length ) => console .log ('Todo count:' , length),
{ equalityFn : (a, b ) => a === b }
)
Transient Updates
interface ScrubbingState {
position : number
updatePosition : (pos : number ) => void
}
const useScrubbingStore = create<ScrubbingState >((set ) => ({
position : 0 ,
updatePosition : (pos ) => set ({ position : pos }, true ),
}))
useScrubbingStore.getState ().updatePosition (50 )
Batching Updates const useTodoStore = create<TodoState >((set ) => ({
todos : [],
batchUpdate : (updates : Partial <TodoState >[] ) => {
set ((state ) => {
let newState = { ...state }
updates.forEach (update => {
newState = { ...newState, ...update }
})
return newState
})
},
}))
Testing Strategies
Mock Stores
import { create } from 'zustand'
import { render, screen, fireEvent } from '@testing-library/react'
import { Counter } from '@/components/Counter'
import { useCounterStore } from '@/stores/useCounterStore'
jest.mock ('@/stores/useCounterStore' )
describe ('Counter' , () => {
beforeEach (() => {
const mockStore = create<CounterState >((set ) => ({
count : 0 ,
increment : jest.fn (() => set ((state ) => ({ count : state.count + 1 }))),
decrement : jest.fn (),
}))
useCounterStore.mockImplementation (mockStore)
})
it ('increments count' , () => {
render (<Counter /> )
fireEvent.click (screen.getByText ('+' ))
expect (screen.getByText ('Count: 1' )).toBeInTheDocument ()
})
})
Test Utilities
import { create } from 'zustand'
export function createTestStore<T>(initialState : Partial <T>) {
return create<T>(() => initialState as T)
}
const testStore = createTestStore<TodoState >({
todos : [
{ id : '1' , text : 'Test todo' , completed : false }
]
})
Reset Store Between Tests
const initialState = { count : 0 }
export const useCounterStore = create<CounterState >((set ) => ({
...initialState,
increment : () => set ((state ) => ({ count : state.count + 1 })),
reset : () => set (initialState),
}))
afterEach (() => {
useCounterStore.getState ().reset ()
})
Migration Guides
From Redux
const counterSlice = createSlice ({
name : 'counter' ,
initialState : { value : 0 },
reducers : {
increment : (state ) => { state.value += 1 },
decrement : (state ) => { state.value -= 1 },
},
})
const useCounterStore = create<CounterState >((set ) => ({
value : 0 ,
increment : () => set ((state ) => ({ value : state.value + 1 })),
decrement : () => set ((state ) => ({ value : state.value - 1 })),
}))
const dispatch = useDispatch ()
const value = useSelector ((state ) => state.counter .value )
dispatch (increment ())
const { value, increment } = useCounterStore ()
increment ()
From Context API
const ThemeContext = createContext<ThemeContextType >(null !)
export function ThemeProvider ({ children }: { children: ReactNode } ) {
const [theme, setTheme] = useState<Theme >('light' )
return (
<ThemeContext.Provider value ={{ theme , setTheme }}>
{children}
</ThemeContext.Provider >
)
}
export const useTheme = ( ) => useContext (ThemeContext )
export const useThemeStore = create<ThemeState >((set ) => ({
theme : 'light' ,
setTheme : (theme ) => set ({ theme }),
}))
const { theme, setTheme } = useThemeStore ()
Next.js Integration
App Router (RSC)
import { create } from 'zustand'
import { persist } from 'zustand/middleware'
export const useCartStore = create<CartState >()(
persist (
(set ) => ({
items : [],
addItem : (item ) => set ((state ) => ({
items : [...state.items , item]
})),
}),
{
name : 'cart-storage' ,
skipHydration : true ,
}
)
)
'use client'
import { useCartStore } from '@/stores/useCartStore'
import { useEffect } from 'react'
export function Cart ( ) {
const { items, addItem } = useCartStore ()
useEffect (() => {
useCartStore.persist .rehydrate ()
}, [])
return <div > {items.length} items</div >
}
Server Actions Integration
'use server'
import { revalidatePath } from 'next/cache'
export async function syncCartToServer (items : CartItem [] ) {
await db.cart .upsert ({
where : { userId : 'current-user' },
update : { items },
create : { userId : 'current-user' , items },
})
revalidatePath ('/cart' )
}
export const useCartStore = create<CartState >((set ) => ({
items : [],
addItem : async (item) => {
set ((state ) => ({ items : [...state.items , item] }))
const items = useCartStore.getState ().items
await syncCartToServer (items)
},
}))
SSR Hydration
import { CartStoreProvider } from '@/providers/CartStoreProvider'
export default function RootLayout ({ children }: { children: ReactNode } ) {
return (
<html >
<body >
<CartStoreProvider >
{children}
</CartStoreProvider >
</body >
</html >
)
}
'use client'
import { useRef } from 'react'
import { useCartStore } from '@/stores/useCartStore'
export function CartStoreProvider ({ children }: { children: ReactNode } ) {
const initialized = useRef (false )
if (!initialized.current ) {
useCartStore.setState ({ items : [] })
initialized.current = true
}
return <> {children}</>
}
Best Practices
Store Organization
const useAuthStore = create<AuthState >(...)
const useTodoStore = create<TodoState >(...)
const useUIStore = create<UIState >(...)
const useAppStore = create<AppState >(...)
Action Naming
const useStore = create ((set ) => ({
addTodo : (text ) => set (...),
removeTodo : (id ) => set (...),
toggleTodo : (id ) => set (...),
}))
const useStore = create ((set ) => ({
todo : (text ) => set (...),
update : (id ) => set (...),
}))
Selector Optimization
const user = useStore ((state ) => state.user )
const theme = useStore ((state ) => state.theme )
const state = useStore ()
Error Handling
interface State {
data : Data | null
loading : boolean
error : Error | null
fetchData : () => Promise <void >
}
const fetchData = async ( ) => {
try {
const data = await api.getData ()
set ({ data })
} catch (error) {
}
}
Common Patterns
Loading States interface ResourceState <T> {
data : T | null
loading : boolean
error : Error | null
status : 'idle' | 'loading' | 'success' | 'error'
}
function createResourceStore<T>() {
return create<ResourceState <T>>((set ) => ({
data : null ,
loading : false ,
error : null ,
status : 'idle' ,
fetch : async () => {
set ({ loading : true , status : 'loading' , error : null })
try {
const data = await fetchData ()
set ({ data, loading : false , status : 'success' })
} catch (error) {
set ({ error, loading : false , status : 'error' })
}
},
}))
}
Undo/Redo interface HistoryState <T> {
past : T[]
present : T
future : T[]
set : (state : T ) => void
undo : () => void
redo : () => void
}
function createHistoryStore<T>(initialState : T) {
return create<HistoryState <T>>((set ) => ({
past : [],
present : initialState,
future : [],
set : (newPresent ) => set ((state ) => ({
past : [...state.past , state.present ],
present : newPresent,
future : [],
})),
undo : () => set ((state ) => {
if (state.past .length === 0 ) return state
const previous = state.past [state.past .length - 1 ]
const newPast = state.past .slice (0 , -1 )
return {
past : newPast,
present : previous,
future : [state.present , ...state.future ],
}
}),
redo : () => set ((state ) => {
if (state.future .length === 0 ) return state
const next = state.future [0 ]
const newFuture = state.future .slice (1 )
return {
past : [...state.past , state.present ],
present : next,
future : newFuture,
}
}),
}))
}
Comparison with Alternatives
vs Redux
No boilerplate (no actions, reducers, dispatch)
No provider needed
Smaller bundle size (~1kb vs ~20kb)
Simpler async handling
TypeScript inference works out of the box
Time-travel debugging
Larger ecosystem and middleware
Strict unidirectional data flow
Better for very large applications
vs Context API
No provider hell
Better performance (no re-render entire subtree)
Simpler API
Built-in middleware
Built into React (no dependency)
Better for component-local state
Explicit component boundaries
vs Jotai
More traditional store-based approach
Better for complex state logic
Easier migration from Redux
Atomic state management
Better code splitting
More React-like (atom-based)
Suspense support out of the box
Resources
Related Skills When using Zustand, these skills enhance your workflow:
react : React integration patterns and hooks for Zustand stores
tanstack-query : Server-state management (use with Zustand for client state)
nextjs : Zustand with Next.js App Router and Client Components
test-driven-development : Testing Zustand stores, actions, and selectors
[Full documentation available in these skills if deployed in your bundle]