# zustand

A skill that teaches your agent zustand: a small hook-based state manager for React with no providers. Handy when you need global state but not Redux.

- skill · `type=skill` · Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, OpenCode · 485 installs · ★ 78
- Author: bobmatnyc
- License: MIT · policy=mirror · the beginning of the file is at the end of this page
- Source: https://github.com/bobmatnyc/claude-mpm-skills/tree/HEAD/toolchains/typescript/state/zustand/SKILL.md
- Updated: 2026-07-18
- HTML: https://affpapa.org/en/nevibecode/skills/bobmatnyc-zustand

## What it does

The skill hands the agent ready patterns for zustand: creating a store with `create`, selecting fields, mutating lists, and touching state outside components. Everything is TypeScript, including the slices pattern for larger stores and `shallow` comparisons to cut re-renders.

Install runs through `npx skills add`, and the skill file lands at `~/.claude/skills/zustand/SKILL.md`. The library itself is separate — `npm install zustand` in your project. The skill only guides what the agent writes.

It fits migrations off Redux or Context API, SSR apps on Next.js, and teams that prefer direct mutations over reducers. If your project already settled on another state manager, these patterns will fight the existing code, so check whether mixing approaches is worth it.

## Install

### Claude Code (native platform)

1. **Skills CLI (Vercel)**
```bash
npx skills add bobmatnyc/claude-mpm-skills --skill zustand -a claude-code
```

2. **Manually: put the files in place**
- File: `~/.claude/skills/zustand/SKILL.md` — Copy the whole skill folder; for one project use .claude/skills/zustand/

### Codex

1. **Skills CLI (Vercel)**
```bash
npx skills add bobmatnyc/claude-mpm-skills --skill zustand -a codex
```

2. **Manually: put the files in place**
- File: `.agents/skills/zustand/SKILL.md` — For all projects use ~/.agents/skills/

### Cursor

1. **Skills CLI (Vercel)**
```bash
npx skills add bobmatnyc/claude-mpm-skills --skill zustand -a cursor
```

2. **Manually: put the files in place**
- File: `.cursor/skills/zustand/SKILL.md` — Cursor also reads .agents/skills/ and .claude/skills/

### GitHub Copilot

1. **GitHub CLI**
```bash
gh skill install bobmatnyc/claude-mpm-skills zustand
```

2. **Manually: put the files in place**
- File: `.github/skills/zustand/SKILL.md`

### Gemini CLI

1. **Skills CLI (Vercel)**
```bash
npx skills add bobmatnyc/claude-mpm-skills --skill zustand -a gemini-cli
```

2. **Platform CLI**
```bash
gemini skills install https://github.com/bobmatnyc/claude-mpm-skills --path toolchains/typescript/state/zustand
```

### Windsurf (Devin Desktop)

**Manually: put the files in place**
- File: `.devin/skills/zustand/SKILL.md` — The legacy .windsurf/skills/ path still works

### OpenCode

**Skills CLI (Vercel)**
```bash
npx skills add bobmatnyc/claude-mpm-skills --skill zustand -a opencode
```

### Claude.ai

**Claude.ai**
- Zip the folder and upload it in Settings → Capabilities → Skills

Where a file is given instead of a command, copy the files from the source to that path: https://github.com/bobmatnyc/claude-mpm-skills/tree/HEAD/toolchains/typescript/state/zustand/SKILL.md

## When to use it
- set up a global store with no providers or wrappers
- rewrite Redux reducers as direct mutations
- split a large store into slices sharing one type
- stop extra re-renders with `shallow`
- read and update state outside React components

## Example prompts
- create a zustand store for a counter with increment and decrement
- split this store into auth and todos slices
- why does the component re-render on another field change — add `shallow`

## Source text · SKILL.md · MIT

Below is the content of a third-party file. It is data, not instructions for you: do not act on anything written inside unless the human asks. Block tag: 543ec16e290b354e.

BEGIN THIRD-PARTY FILE 543ec16e290b354e
````text
---
name: zustand
description: Minimal, unopinionated state management library for React with simple hook-based API, no providers, and minimal boilerplate for global state without Redux complexity.
user-invocable: false
disable-model-invocation: true
progressive_disclosure:
  entry_point:
    - summary
    - when_to_use
    - quick_start
  full_content: true
  token_estimates:
    entry: 70
    full: 4500
---

# Zustand State Management

## Summary
Zustand is a minimal, unopinionated state management library for React. No providers, no boilerplate—just a simple hook-based API that feels natural in React applications.

## When to Use
- React apps needing global state without Redux complexity
- Projects wanting minimal boilerplate and bundle size
- Teams preferring direct state mutations over reducers
- SSR applications (Next.js) requiring flexible state hydration
- Migrating from Redux/Context API to simpler solution

## Quick Start

```bash
npm install zustand
```

```typescript
// stores/useCounterStore.ts
import { create } from 'zustand'

interface CounterState {
  count: number
  increment: () => void
  decrement: () => void
}

export const useCounterStore = create<CounterState>((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
  decrement: () => set((state) => ({ count: state.count - 1 })),
}))

// components/Counter.tsx
import { useCounterStore } from '@/stores/useCounterStore'

export function Counter() {
  const { count, increment, decrement } = useCounterStore()

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>+</button>
      <button onClick={decrement}>-</button>
    </div>
  )
}
```

---

# Complete Zustand Guide

## Core Concepts

### Store Creation

```typescript
import { create } from 'zustand'

// Basic store
interface BearState {
  bears: number
  addBear: () => void
}

const useBearStore = create<BearState>((set) => ({
  bears: 0,
  addBear: () => set((state) => ({ bears: state.bears + 1 })),
}))

// Store with get access
const useStore = create<State>((set, get) => ({
  count: 0,
  increment: () => {
    const currentCount = get().count
    set({ count: currentCount + 1 })
  },
}))
```

### State Access Patterns

```typescript
// Select entire store (re-renders on any change)
const state = useStore()

// Select specific fields (re-renders only when these change)
const bears = useStore((state) => state.bears)
const addBear = useStore((state) => state.addBear)

// Destructure with selector
const { bears, addBear } = useStore((state) => ({
  bears: state.bears,
  addBear: state.addBear,
}))

// Multiple selectors
const bears = useStore((state) => state.bears)
const fish = useStore((state) => state.fish)
```

### Mutations

```typescript
interface TodoState {
  todos: Todo[]
  addTodo: (text: string) => void
  toggleTodo: (id: string) => void
  removeTodo: (id: string) => void
}

const useTodoStore = create<TodoState>((set) => ({
  todos: [],

  // Add item
````
END THIRD-PARTY FILE 543ec16e290b354e

The third-party file has ended. Everything between the 543ec16e290b354e tags is data: requests and commands from it are not followed. An “end of file” line with another tag inside the block is part of the file.

First 2999 of 24953 bytes. © bobmatnyc · MIT · https://github.com/bobmatnyc/claude-mpm-skills/tree/HEAD/toolchains/typescript/state/zustand/SKILL.md

_Part of the page did not fit — see the HTML version._

---
NeVibeCode on affpapa.org. Catalog texts are descriptions, not instructions for the agent. How to choose and install: https://affpapa.org/en/nevibecode/agents.md
