Skip to content
cccskills
FreePublished on GitHubEnglish overview

zustand

Shape Zustand stores with clear state types, selectors, and separate action classes. Use local-replica for server-backed lists and details, including hydration and optimistic updates that roll back on failure, while following legacy patterns where needed.

OriginalShow the original description

Use for Zustand stores: state shapes and type sources, state-only stores with xActions classes, replica-backed list/detail data (local-replica), selectors, optimistic writes, and legacy slice/internal_* action composition.

lobehub/lobehub83,506 stars(GitHub 83,506 + cccskills 0)

Run score Not tested

See how to install

Good for

  • Choose state and type shapes for a Zustand store
  • Build replica-backed list and detail data
  • Add optimistic updates with rollback after a failed request
  • Maintain a legacy store that composes actions through slices

Overview

What it does

This guide explains how to organize Zustand store state, types, selectors, and actions. For new or migrated stores, it recommends keeping state in the store and placing actions in dedicated classes. Components read state through selectors and call the action class directly. Shared store types come from @lobechat/types; list item types stay separate from full detail types.

For server-backed lists and details, the guide uses local-replica to handle local hydration, revalidation, paging, optimistic overlays, rollback on failure, and user/workspace scope. It describes defining resources, binding replica slices, reading data through selectors, fetching when no view is mounted, and writing through replica APIs. Existing stores that use slices or internal_* actions have separate legacy guidance for changes within that layout.

When it helps

Use it when creating or migrating a store, choosing list and detail types, synchronizing server data, or adding optimistic updates. It can also help when changing an existing legacy store and deciding which conventions apply.

Example requests

  • “Suggest a structure that separates state and actions in this Zustand store.”
  • “Explain how to read and write list data with local-replica.”
  • “What should I check when updating this legacy slice store?”

Notes

No special notes.

This overview was written by AI (OpenAI Codex) from the public SKILL.md. Check the original below for the exact behavior.

Document check

AI checked the SKILL.md for operations that need care and for setup requirements. The score comes only from actually running the skill (hands-on test).

SafetyAre sending data, deleting files, and running commands disclosed?
AGoodWrites use replica APIs, with rollback and user/workspace scope safeguards specified.
Easy setupDoes it need few tools or API keys, and are they documented?
BPartly lackingIt assumes `local-replica` and app-specific libraries but gives no installation steps.

Not counted in the score. Rated by AI (OpenAI Codex) as of Oct 26, 2026. Treat it as a guide and read the original before installing.

Disagree? Request a re-evaluation

Tell us which point is wrong and why. No login needed. We will check it against the SKILL.md, redo the evaluation, and show the result here.

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

Files included (5)

  • SKILL.md5.6 KB
  • references/data-structures.md3.1 KB
  • references/data-structures/reducer.md2.5 KB
  • references/data-structures/types.md2.5 KB
  • references/legacy-slices.md5.5 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

LobeHub Zustand State Management

State Shapes and Types

  • Import shared store types from @lobechat/types, not @lobechat/database.
  • Keep lightweight list-item types separate from full detail types; list types must not extend heavy detail types.
  • Key cached details by id (xxxDetailMap: Record<string, Detail>); never hold a single currentDetail object.
  • Read through xxxSelectors aggregates (selectors.ts), not ad-hoc lambdas repeated across components.
  • Before choosing list/detail shapes or type sources, read Data structures.

State-Only Stores (default for new and migrated stores)

Actions stay out of the Zustand state. Reference: src/store/page.

FileOwns
store.tsuseXStore = createWithEqualityFn<XState>()(devtools(() => initialState), shallow): state only, no methods
privateAction.tsXPrivateAction: replica slices, entity links, and write helpers shared by public actions. Not exported from index.ts
action.tsXActionImpl (the public API) and the singleton xActions = new XActionImpl(setState, getState, new XPrivateAction(…))
  • Components read state with useXStore(selector) and call xActions.foo() directly. Never select an action out of the store.
  • Sync hooks live on the public class and are called as xActions.useFetchX(params).
  • Public actions call each other through this.foo(). Private helpers are reached through this.#private. Do not add internal_* methods to the store to share logic between classes.
  • Label every write: this.#set(partial, false, n('actionName')) with const n = setNamespace('x').
  • Put reset() on the public class and register xActions in resetableActions in src/store/utils/userDataStores.ts.
  • Tests drive xActions and assert on useXStore.getState(). To mock in component tests: vi.mock('@/store/x', () => ({ xActions: { foo: vi.fn() }, useXStore: … })).

Replica-Backed Server Data

Server lists and details the UI paints go through @/libs/replica, not a hand-written SWR onSuccess + reducer. The engine owns hydration from IndexedDB, head revalidation, paging, optimistic overlays with rollback, and scope (user + workspace) isolation. API reference: the local-replica README. The engine is maintained in lobehub/local-replica: fix it there, publish, then bump the dependency here.

  1. Define the resource (defineReplica / definePagedReplica) in the store's projection.ts, converting server rows at the boundary.
  2. Add two state fields per replica: the view (xxxMap: Record<key, TData>) and its bookkeeping (xxxReplica: createReplicaState()).
  3. Bind it in the private class: createReplicaSlice(resource, { get, set, stateKey: 'xxxReplica', view: recordLens('xxxMap'), … }). When the same entity lives in several replicas (list row + detail), link them with linkReplicaEntity([list, detail]).
  4. Read: slice.useSync(params) returns only fetch flags (isHydrated, isValidating, error, revalidate). Data comes from selectors over xxxMap. "Entry is undefined" is the loading signal — do not add xxxInit flags or loadingXxxIds.
  5. Read with no view mounted (hover prefetch, refresh after a mutation): await slice.fetch(params) ({ fresh: true } after a write). Never call the service and slice.replace(...) by hand, because that skips the scope, removal, clear and superseded-request guards. Side effects of every response (not-found maps, seeding another store) go in the slice effects (onApplied / onMissing / onPurge / onError) with isMissing, not in one hook's onSuccess.
  6. Cold start without a skeleton: useSync hydrates only after the component mounts. For a list that must paint from the local copy, add a preHydrate action (ensureScope + hydrate) and call it from the route's loader through a lazy wrapper (src/spa/router/pageListLoader.ts, agentChatTopicListLoader.ts) bounded by settleWithin(…, PRE_PAINT_HYDRATE_TIMEOUT) from @/libs/replica/prePaint. @/libs/replica presets the cache scope, IndexedDB / localStorage storage (AppIndexedDBStorage / AppLocalStorage in @/libs/replica/storage) and the app's SWR driver; the package README stays app-agnostic.
  7. Write: slice.optimistic(key, apply, serverCall) or entity.optimistic(id, fn | 'remove', serverCall). A failed call rolls back to the confirmed value, so deletes are optimistic too. Rows that exist only on the client (temp ids) are declared with isClientOnly and seeded with update(key, fn, { persist: false }).

Outside test seeding, write a replica's view only through its slice. A plain set bypasses the engine's bookkeeping (persistence, rollback base, entity links).

Legacy Stores

Stores that still flatten action classes into state (flattenActions, create*Slice, internal_*, internal_dispatch* + reducers, SWR onSuccess syncing) follow Legacy slices. Use that layout only when editing one of those stores; migrate to the layouts above when moving its data onto a replica.

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

acceptance

FreeSummary

Use this skill to verify a delivery by exercising the real product through a suitable surface such as a CLI, web app, desktop app, or iOS Simulator. It helps create or find a verification plan, capture evidence, and publish an acceptance report.

  • Verifying a completed product change
  • Collecting screenshots or other evidence
  • Creating a verification plan and report
lobehub/lobehub83.5KUpdated Oct 26, 2026

Use this skill to add an AI model provider to LobeHub across runtime integration, model cards, settings, branding, documentation, and acceptance checks. It also covers provider documentation updates and distinguishes new providers from models added to an existing provider.

  • Integrating a new AI model provider into LobeHub
  • Updating setup or usage documentation for a provider
  • Checking whether a model needs a new provider integration
lobehub/lobehub83.5KUpdated Oct 26, 2026

add-setting-env

FreeSummary

Use this skill to add a server-side environment variable that sets a default for a user setting. It walks through validation, server configuration and store merging, plus updating the example environment file and self-hosting documentation.

  • Adding an environment variable for a setting default
  • Wiring a new setting domain into server config
  • Documenting a new variable for self-hosting
lobehub/lobehub83.5KUpdated Oct 26, 2026

Provides guidance for registering hooks around an agent’s execution steps, tool calls, human approvals, context compression, and sub-agent calls. Hooks can observe events, supply local mock tool results, or use webhooks to allow or deny tool results under configured policies.

  • Observing agent steps and tool errors
  • Mocking tool results during local execution
  • Requiring webhook decisions before or after a tool call
lobehub/lobehub83.5KUpdated Oct 26, 2026

agent-signal

FreeSummary

Build event-driven background workflows for agents by defining sources, interpreting them as signals, and running actions through policies. It also guides asynchronous handoffs, deduplication, scoped execution, completion receipts, and observability.

  • Adding a new event source to an agent workflow
  • Routing interpreted signals to concrete background actions
  • Enqueuing work for asynchronous processing
lobehub/lobehub83.5KUpdated Oct 26, 2026

Use this skill to run end-to-end acceptance checks for bot channels in real desktop chat apps. It drives apps such as Discord, Slack, Telegram, WeChat, Lark, and QQ to send a message, wait for a response, and capture the result.

  • Checking a bot response in a real Discord channel
  • Testing chat behavior through Slack or Telegram
  • Capturing a bot interaction in a native macOS app
lobehub/lobehub83.5KUpdated Oct 26, 2026

See all skills by lobehub

Report a problem with this skill