skills/zustand/SKILL.md
Use and structure Zustand stores for React and vanilla JS state management. Use when the user mentions Zustand, needs a store pattern, global state, persist state, or migrating from Redux/Context.
npx skillsauth add tianyili/skills zustandInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Guidance for using Zustand (create, stores, middleware, and scaling patterns). No provider required.
A file-scoped rule is provided at rules/zustand-stores.mdc. Copy it to your project’s .cursor/rules/ to get automatic reminders when editing store files.
import { create } from 'zustand'
const useStore = create((set) => ({
count: 0,
inc: () => set((s) => ({ count: s.count + 1 })),
reset: () => set({ count: 0 }),
}))
set((s) => ({ ... })).get().count).set((s) => ({ count: s.count + 1 })).Select only what the component needs to avoid unnecessary re-renders.
// Bad – re-renders on any store change
const state = useStore()
// Good – re-renders only when count changes
const count = useStore((s) => s.count)
const inc = useStore((s) => s.inc)
Use shallow when selecting multiple fields:
import { useShallow } from 'zustand/react/shallow'
const { count, inc } = useStore(useShallow((s) => ({ count: s.count, inc: s.inc })))
import { create } from 'zustand'
import { persist, createJSONStorage } from 'zustand/middleware'
const useStore = create(
persist(
(set) => ({
token: null,
setToken: (t) => set({ token: t }),
}),
{
name: 'app-storage',
storage: createJSONStorage(() => localStorage),
partialize: (s) => ({ token: s.token }), // optional: persist only part
},
),
)
For async storage (e.g. React Native), use a storage adapter that returns Promises; the store will rehydrate when the promise resolves.
import { devtools } from 'zustand/middleware'
const useStore = create(
devtools(
(set) => ({ ... }),
{ name: 'MyStore' },
),
)
Order: store creator innermost, then devtools, then persist (so persist wraps the devtools-wrapped store).
create(
persist(
devtools((set) => ({ ... }), { name: 'AppStore' }),
{ name: 'app-storage' },
),
)
Split one store into slice creators; compose them in a single create call.
const createAuthSlice = (set) => ({
user: null,
login: (user) => set({ user }),
logout: () => set({ user: null }),
})
const createUiSlice = (set) => ({
theme: 'light',
setTheme: (theme) => set({ theme }),
})
const useStore = create((...a) => ({
...createAuthSlice(...a),
...createUiSlice(...a),
}))
Each slice receives (set, get, store) and returns a plain object; avoid naming collisions between slices.
Use immer middleware when updaters are complex or nested.
import { immer } from 'zustand/middleware/immer'
const useStore = create(
immer((set) => ({
nested: { a: 1, b: { c: 2 } },
updateC: (v) => set((s) => {
s.nested.b.c = v
}),
})),
)
Type the store state and actions; infer hook types from create.
interface BearState {
bears: number
addBear: () => void
}
const useBearStore = create<BearState>()((set) => ({
bears: 0,
addBear: () => set((s) => ({ bears: s.bears + 1 })),
}))
Use create<State>()(...) so middlewares keep correct typing.
Same store can be used without React: useStore.getState(), useStore.setState(...), useStore.subscribe((state) => { ... }).
useShallow for multiple fields.partialize with persist when the store has non-serializable values (e.g. functions, class instances).| Need | Use |
|-------------------|------------------------------|
| Basic store | create((set) => ({ ... })) |
| Persist | persist(..., { name, storage }) |
| DevTools | devtools(...) |
| Nested updates | immer |
| Scale store | Slice functions + spread in create |
| Fewer re-renders | Selectors or useShallow |
tools
Use when displaying token balances or token values in UI, converting user input to bigint for contract writes, or doing math with wei/token amounts in viem or wagmi apps. Web3 number formatting with formatUnits, parseUnits, BigNumber, bigint naming (`Raw`, `Formatted`, `Bn`), decimals handling, and reusable display helpers. Triggers on token balance, formatUnits, parseUnits, wei, decimals, bigint, BigNumber, number formatting, or web3 display math.
development
React performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React code to ensure optimal performance patterns. Triggers on tasks involving React components, data fetching, bundle optimization, or performance improvements.
development
Use when designing or refactoring functions with many optional params, configurable components, or when the user mentions optional params, function signatures, or API simplification. Reduces optional parameters in functions and components for cleaner APIs.
development
Implements data tables using @tanstack/react-table v8 with project UI primitives. Use when adding or modifying tables, column configs, sorting, filtering, row selection, or virtualized table bodies.