Back to skills
SkillHub ClubShip Full StackFull StackFrontend

zustand-store

Create Zustand stores with TypeScript, subscribeWithSelector middleware, and proper state/action separation. Use when building React state management, creating global stores, or implementing reactive state patterns with Zustand.

Packaged view

This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.

Stars
1,779
Hot score
99
Updated
March 20, 2026
Overall rating
C4.8
Composite score
4.8
Best-practice grade
A92.0

Install command

npx @skill-hub/cli install microsoft-agent-skills-zustand-store

Repository

microsoft/agent-skills

Skill path: .github/skills/zustand-store

Create Zustand stores with TypeScript, subscribeWithSelector middleware, and proper state/action separation. Use when building React state management, creating global stores, or implementing reactive state patterns with Zustand.

Open repository

Best for

Primary workflow: Ship Full Stack.

Technical facets: Full Stack, Frontend.

Target audience: everyone.

License: Unknown.

Original source

Catalog source: SkillHub Club.

Repository owner: microsoft.

This is still a mirrored public skill entry. Review the repository before installing into production workflows.

What it helps with

  • Install zustand-store into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
  • Review https://github.com/microsoft/agent-skills before adding zustand-store to shared team environments
  • Use zustand-store for development workflows

Works across

Claude CodeCodex CLIGemini CLIOpenCode

Favorites: 0.

Sub-skills: 0.

Aggregator: No.

Original source / Raw SKILL.md

---
name: zustand-store
description: Create Zustand stores with TypeScript, subscribeWithSelector middleware, and proper state/action separation. Use when building React state management, creating global stores, or implementing reactive state patterns with Zustand.
---

# Zustand Store

Create Zustand stores following established patterns with proper TypeScript types and middleware.

## Quick Start

Copy the template from [assets/template.ts](assets/template.ts) and replace placeholders:
- `{{StoreName}}` → PascalCase store name (e.g., `Project`)
- `{{description}}` → Brief description for JSDoc

## Always Use subscribeWithSelector

```typescript
import { create } from 'zustand';
import { subscribeWithSelector } from 'zustand/middleware';

export const useMyStore = create<MyStore>()(
  subscribeWithSelector((set, get) => ({
    // state and actions
  }))
);
```

## Separate State and Actions

```typescript
export interface MyState {
  items: Item[];
  isLoading: boolean;
}

export interface MyActions {
  addItem: (item: Item) => void;
  loadItems: () => Promise<void>;
}

export type MyStore = MyState & MyActions;
```

## Use Individual Selectors

```typescript
// Good - only re-renders when `items` changes
const items = useMyStore((state) => state.items);

// Avoid - re-renders on any state change
const { items, isLoading } = useMyStore();
```

## Subscribe Outside React

```typescript
useMyStore.subscribe(
  (state) => state.selectedId,
  (selectedId) => console.log('Selected:', selectedId)
);
```

## Integration Steps

1. Create store in `src/frontend/src/store/`
2. Export from `src/frontend/src/store/index.ts`
3. Add tests in `src/frontend/src/store/*.test.ts`


---

## Referenced Files

> The following files are referenced in this skill and included for context.

### assets/template.ts

```typescript
import { create } from 'zustand';
import { subscribeWithSelector } from 'zustand/middleware';

// ============================================================================
// Types
// ============================================================================

/**
 * {{StoreName}}State - The state shape for this store
 */
export interface {{StoreName}}State {
  // Add state properties
  items: unknown[];
  selectedId: string | null;
  isLoading: boolean;
  error: string | null;
}

/**
 * {{StoreName}}Actions - Actions that can be performed on this store
 */
export interface {{StoreName}}Actions {
  // Setters
  setItems: (items: unknown[]) => void;
  setSelectedId: (id: string | null) => void;
  setLoading: (loading: boolean) => void;
  setError: (error: string | null) => void;

  // Complex actions
  loadItems: () => Promise<void>;
  addItem: (item: unknown) => void;
  removeItem: (id: string) => void;

  // Reset
  reset: () => void;
}

/**
 * {{StoreName}}Store - Combined store type
 */
export type {{StoreName}}Store = {{StoreName}}State & {{StoreName}}Actions;

// ============================================================================
// Initial State
// ============================================================================

const initialState: {{StoreName}}State = {
  items: [],
  selectedId: null,
  isLoading: false,
  error: null,
};

// ============================================================================
// Store
// ============================================================================

/**
 * use{{StoreName}}Store - Zustand store for managing {{description}}
 *
 * @example
 * ```typescript
 * // In a component - use individual selectors for performance
 * const items = use{{StoreName}}Store((state) => state.items);
 * const loadItems = use{{StoreName}}Store((state) => state.loadItems);
 *
 * // Subscribe to changes outside React
 * use{{StoreName}}Store.subscribe(
 *   (state) => state.selectedId,
 *   (selectedId) => console.log('Selected:', selectedId)
 * );
 * ```
 */
export const use{{StoreName}}Store = create<{{StoreName}}Store>()(
  subscribeWithSelector((set, get) => ({
    // Initial state
    ...initialState,

    // Simple setters
    setItems: (items) => set({ items }),
    setSelectedId: (selectedId) => set({ selectedId }),
    setLoading: (isLoading) => set({ isLoading }),
    setError: (error) => set({ error }),

    // Async action example
    loadItems: async () => {
      set({ isLoading: true, error: null });
      try {
        // const items = await fetchItems();
        const items: unknown[] = []; // Replace with actual fetch
        set({ items, isLoading: false });
      } catch (error) {
        set({
          error: error instanceof Error ? error.message : 'Failed to load',
          isLoading: false,
        });
      }
    },

    // Add item (immutable update)
    addItem: (item) => {
      set({ items: [...get().items, item] });
    },

    // Remove item (immutable update)
    removeItem: (id) => {
      set({
        items: get().items.filter((item) => (item as { id: string }).id !== id),
        // Clear selection if removed item was selected
        selectedId: get().selectedId === id ? null : get().selectedId,
      });
    },

    // Reset to initial state
    reset: () => set(initialState),
  }))
);

```