.agents/skills/zustand-x/SKILL.md
Type-safe Zustand state management with auto-generated hooks, selectors, and actions. Use when implementing or working with Zustand stores in React apps, especially when creating new stores, adding selectors/actions, using middleware (devtools, persist, immer, mutative), or migrating from plain Zustand to get better DX with less boilerplate.
npx skillsauth add jkker/react-template zustand-xInstall 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.
Type-safe state management built on Zustand with auto-generated hooks, simplified API patterns, and extensible selectors/actions.
Create a basic store:
import { createStore, useStoreValue, useStoreState } from 'zustand-x'
const repoStore = createStore({
name: 'ZustandX',
stars: 0,
})
function RepoInfo() {
const name = useStoreValue(repoStore, 'name')
const stars = useStoreValue(repoStore, 'stars')
return (
<div>
<h1>{name}</h1>
<p>{stars} stars</p>
</div>
)
}
function AddStarButton() {
const [, setStars] = useStoreState(repoStore, 'stars')
return <button onClick={() => setStars((s) => s + 1)}>Add star</button>
}
const userStore = createStore(
{
name: 'Alice',
loggedIn: false,
},
{
name: 'user',
devtools: true, // Redux DevTools
persist: true, // localStorage persistence
mutative: true, // Immer-style mutations
},
)
// Get single value
store.get('name') // => 'Alice'
// Get entire state
store.get('state')
// Call selector with arguments
store.get('someSelector', 1, 2)
// Set single value
store.set('name', 'Bob')
// Call action
store.set('someAction', 10)
// Update multiple values
store.set('state', (draft) => {
draft.name = 'Bob'
draft.loggedIn = true
return draft // Required with immer
})
// Subscribe to field
const unsubscribe = store.subscribe('name', (name, previousName) => {
console.log('Name changed from', previousName, 'to', name)
})
// Subscribe to state
const unsubscribe = store.subscribe('state', (state) => {
console.log('State changed:', state)
})
// Fire immediately on subscribe
const unsubscribe = store.subscribe(
'name',
(name) => name.length,
(length) => console.log('Name length:', length),
{ fireImmediately: true },
)
useStoreValue(store, key, ...args)Subscribe to a single value or selector:
const name = useStoreValue(store, 'name')
// With selector arguments
const greeting = useStoreValue(store, 'greeting', 'Hello')
// With custom equality for arrays/objects
const items = useStoreValue(
store,
'items',
(a, b) => a.length === b.length && a.every((item, i) => item.id === b[i].id),
)
useStoreState(store, key, [equalityFn])Get value and setter like useState, perfect for forms:
function UserForm() {
const [name, setName] = useStoreState(store, 'name')
const [email, setEmail] = useStoreState(store, 'email')
return (
<form>
<input value={name} onChange={(e) => setName(e.target.value)} />
<input value={email} onChange={(e) => setEmail(e.target.value)} />
</form>
)
}
useTracked(store, key)Subscribe with minimal re-renders for large objects:
function UserEmail() {
// Only re-renders when user.email changes
const user = useTracked(store, 'user')
return <div>{user.email}</div>
}
Derive computed values from state:
const store = createStore({ firstName: 'Jane', lastName: 'Doe' }, { mutative: true })
const extendedStore = store
.extendSelectors(({ get }) => ({
fullName: () => get('firstName') + ' ' + get('lastName'),
}))
.extendSelectors(({ get }) => ({
fancyTitle: (prefix: string) => prefix + get('fullName').toUpperCase(),
}))
// Usage
extendedStore.get('fullName') // => 'Jane Doe'
extendedStore.get('fancyTitle', 'Hello ') // => 'Hello JANE DOE'
// In components
function Title() {
const fancyTitle = useStoreValue(extendedStore, 'fancyTitle', 'Welcome ')
return <h1>{fancyTitle}</h1>
}
Create functions that modify state:
const storeWithActions = store.extendActions(({ get, set, actions: { someActionToOverride } }) => ({
updateName: (newName: string) => set('name', newName),
resetState: () => {
set('state', (draft) => {
draft.firstName = 'Jane'
draft.lastName = 'Doe'
return draft
})
},
someActionToOverride: () => {
// Override or call original: someActionToOverride()
},
}))
// Usage
storeWithActions.set('updateName', 'Julia')
storeWithActions.set('resetState')
const store = createStore(
{ name: 'ZustandX', stars: 10 },
{
name: 'repo',
devtools: { enabled: true }, // Redux DevTools with options
persist: { enabled: true }, // localStorage with options
mutative: true, // shorthand for { enabled: true }
},
)
store.get('name') vs store.getState().namestore.set('name', value) patternextendSelectorsdevelopment
Vitest fast unit testing framework powered by Vite with Jest-compatible API. Use when writing tests, mocking, configuring coverage, or working with test filtering and fixtures.
development
Test-driven development with red-green-refactor loop. Use when user wants to build features or fix bugs using TDD, mentions "red-green-refactor", wants integration tests, or asks for test-first development.
testing
Manage TanStack Intent skill-to-task mappings. Use when adding, updating, or discovering agent skills from installed npm packages, or when re-syncing intent-skills after dependency updates.
development
Manages shadcn components and projects — adding, searching, fixing, debugging, styling, and composing UI. Provides project context, component docs, and usage examples. Applies when working with shadcn/ui, component registries, presets, --preset codes, or any project with a components.json file. Also triggers for "shadcn init", "create an app with --preset", or "switch to --preset".