zustand
Provides architectural patterns and best practices for implementing Zustand stores in React applications.
Install
mkdir -p .claude/skills/zustand && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/626" && unzip -o skill.zip -d .claude/skills/zustand && rm skill.zipInstalls to .claude/skills/zustand
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
LobeHub Zustand store conventions. Use when editing src/store, store slices, public/internal actions, dispatch actions, flattenActions, optimistic updates, selectors, maps, or class action migration.Key capabilities
- →Differentiate between public and internal action logic
- →Implement optimistic update patterns for UI responsiveness
- →Organize state into maps and ID arrays for lookup efficiency
- →Decouple state dispatchers from business logic functions
How it works
Applies a structured hierarchy of public actions, internal logic functions, and dispatchers to maintain separation of concerns within a store.
Inputs & outputs
When to use zustand
- →Implement complex state slices
- →Apply optimistic update patterns
- →Migrate class-based actions to store functions
- →Define internal versus public dispatch methods
About this skill
LobeHub Zustand State Management
Action Type Hierarchy
1. Public Actions
Main interfaces for UI components:
- Naming: Verb form (
createTopic,sendMessage) - Responsibilities: Parameter validation, flow orchestration
2. Internal Actions (internal_*)
Core business logic implementation:
- Naming:
internal_prefix (internal_createTopic) - Responsibilities: Optimistic updates, service calls, error handling
- Should not be called directly by UI
3. Dispatch Methods (internal_dispatch*)
State update handlers:
- Naming:
internal_dispatch+ entity (internal_dispatchTopic) - Responsibilities: Calling reducers, updating store
When to Use Reducer vs Simple set
Use Reducer Pattern:
- Managing object lists/maps (
messagesMap,topicMaps) - Optimistic updates
- Complex state transitions
Use Simple set:
- Toggling booleans
- Updating simple values
- Setting single state fields
Optimistic Update Pattern
internal_createTopic: async (params) => {
const tmpId = Date.now().toString();
// 1. Immediately update frontend (optimistic)
get().internal_dispatchTopic(
{ type: 'addTopic', value: { ...params, id: tmpId } },
'internal_createTopic'
);
// 2. Call backend service
const topicId = await topicService.createTopic(params);
// 3. Refresh for consistency
await get().refreshTopic();
return topicId;
},
Delete operations: Don't use optimistic updates (destructive, complex recovery)
Naming Conventions
Actions:
-
Public:
createTopic,sendMessage -
Internal:
internal_createTopic,internal_updateMessageContent -
Dispatch:
internal_dispatchTopicState: -
ID arrays:
topicEditingIds -
Maps:
topicMaps,messagesMap -
Active:
activeTopicId -
Init flags:
topicsInit
Detailed Guides
- Action patterns:
references/action-patterns.md - Slice organization:
references/slice-organization.md
Class-Based Action Implementation
We are migrating slices from plain StateCreator objects to class-based actions.
Pattern
- Define a class that encapsulates actions and receives
(set, get, api)in the constructor. - Use
#privatefields (e.g.,#set,#get) to avoid leaking internals. - Prefer shared typing helpers:
StoreSetter<T>from@/store/typesforset.Pick<ActionImpl, keyof ActionImpl>to expose only public methods.
- Export a
create*Slicehelper that returns a class instance.
type Setter = StoreSetter<HomeStore>;
export const createRecentSlice = (set: Setter, get: () => HomeStore, _api?: unknown) =>
new RecentActionImpl(set, get, _api);
export class RecentActionImpl {
readonly #get: () => HomeStore;
readonly #set: Setter;
constructor(set: Setter, get: () => HomeStore, _api?: unknown) {
void _api;
this.#set = set;
this.#get = get;
}
useFetchRecentTopics = () => {
// ...
};
}
export type RecentAction = Pick<RecentActionImpl, keyof RecentActionImpl>;
Composition
- In store files, merge class instances with
flattenActions(do not spread class instances). flattenActionsbinds methods to the original class instance and supports prototype methods and class fields.
const createStore: StateCreator<HomeStore, [['zustand/devtools', never]]> = (...params) => ({
...initialState,
...flattenActions<HomeStoreAction>([
createRecentSlice(...params),
createHomeInputSlice(...params),
]),
});
Multi-Class Slices
- For large slices that need multiple action classes, compose them in the slice entry using
flattenActions. - Use a local
PublicActions<T>helper if you need to combine multiple classes and hide private fields.
type PublicActions<T> = { [K in keyof T]: T[K] };
export type ChatGroupAction = PublicActions<
ChatGroupInternalAction & ChatGroupLifecycleAction & ChatGroupMemberAction & ChatGroupCurdAction
>;
export const chatGroupAction: StateCreator<
ChatGroupStore,
[['zustand/devtools', never]],
[],
ChatGroupAction
> = (...params) =>
flattenActions<ChatGroupAction>([
new ChatGroupInternalAction(...params),
new ChatGroupLifecycleAction(...params),
new ChatGroupMemberAction(...params),
new ChatGroupCurdAction(...params),
]);
Store-Access Types
- For class methods that depend on actions in other classes, define explicit store augmentations:
ChatGroupStoreWithSwitchTopicfor lifecycleswitchTopicChatGroupStoreWithRefreshfor member refreshChatGroupStoreWithInternalfor curdinternal_dispatchChatGroup
Slices That Don't Currently Need set
When a slice doesn't write local state (e.g. it delegates to another store or just runs hooks), drop #set and mark the constructor param as _set with void _set to keep the (set, get, api) shape:
export class ToolActionImpl {
readonly #get: () => ConversationStore;
constructor(_set: Setter, get: () => ConversationStore, _api?: unknown) {
void _set;
void _api;
this.#get = get;
}
approveToolCall = async (id: string) => {
const { context, hooks } = this.#get();
await useChatStore.getState().approveToolCalling(id, '', context);
hooks.onToolCallComplete?.(id, undefined);
};
}
- Drop
#setwhen unused; restore it when a later edit needsset— re-adding costs nothing. - Don't add
setNamespacefor slices that don't write state. - Don't keep both old slice objects and class actions active at the same time during migration.
When not to use it
- →Managing simple local component state (use React.useState)
- →High-frequency numerical computation outside of state updates
Prerequisites
Limitations
- →Strict naming conventions for internal vs public methods
- →Requires manual state normalization for complex objects
How it compares
It follows specific LobeHub conventions for naming and action structure rather than generic state management patterns.
Compared to similar skills
zustand side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| zustand (this skill) | 113 | 2mo | No flags | Intermediate |
| react | 34 | 2mo | No flags | Intermediate |
| react-component-patterns | 3 | 1mo | No flags | Intermediate |
| zustand-5 | 8 | 7mo | No flags | Beginner |
Try saying
Example prompts that trigger this skill in your AI assistant.
More by lobehub
View all by lobehub →You might also like
react
lobehub
React component development guide. Use when working with React components (.tsx files), creating UI, using @lobehub/ui components, implementing routing, or building frontend features. Triggers on React component creation, modification, layout implementation, or navigation tasks.
react-component-patterns
HoangNguyen0403
Modern React component architecture and composition patterns.
zustand-5
prowler-cloud
Zustand 5 state management patterns. Trigger: When implementing client-side state with Zustand (stores, selectors, persist middleware, slices).
1k-state-management
OneKeyHQ
Jotai state management patterns — atoms, globalAtom, contextAtom, and persistence.
react-router-getting-started
ssrjkk
Getting Started for React-Router: initial setup and first steps
accessibility-compliance
wshobson
Implement WCAG 2.2 compliant interfaces with mobile accessibility, inclusive design patterns, and assistive technology support. Use when auditing accessibility, implementing ARIA patterns, building for screen readers, or ensuring inclusive user experiences.