Files
gh-jezweb-claude-skills-ski…/references/middleware-guide.md
2025-11-30 08:25:53 +08:00

5.2 KiB

Zustand Middleware Complete Guide

Complete reference for all Zustand middleware. Load when user asks about persistence, devtools, immer, or custom middleware.


Persist Middleware

Basic Usage

import { create } from 'zustand'
import { persist, createJSONStorage } from 'zustand/middleware'

const useStore = create<Store>()(
  persist(
    (set) => ({ /* store */ }),
    { name: 'storage-name' }
  )
)

Storage Options

// localStorage (default)
storage: createJSONStorage(() => localStorage)

// sessionStorage
storage: createJSONStorage(() => sessionStorage)

// Custom storage
storage: createJSONStorage(() => customStorage)

Partial Persistence

persist(
  (set) => ({ /* store */ }),
  {
    name: 'storage',
    partialize: (state) => ({
      theme: state.theme,
      // Don't persist sensitive data
    }),
  }
)

Schema Migration

persist(
  (set) => ({ /* store */ }),
  {
    name: 'storage',
    version: 2,
    migrate: (persistedState: any, version) => {
      if (version === 0) {
        // v0 → v1
        persistedState.newField = 'default'
      }
      if (version === 1) {
        // v1 → v2
        delete persistedState.oldField
      }
      return persistedState
    },
  }
)

Devtools Middleware

Basic Usage

import { devtools } from 'zustand/middleware'

const useStore = create<Store>()(
  devtools(
    (set) => ({ /* store */ }),
    { name: 'StoreName' }
  )
)

Named Actions

increment: () => set(
  (state) => ({ count: state.count + 1 }),
  undefined,
  'counter/increment' // Shows in DevTools
)

Production Toggle

devtools(
  (set) => ({ /* store */ }),
  {
    name: 'Store',
    enabled: process.env.NODE_ENV === 'development'
  }
)

Immer Middleware

Allows mutable state updates (Immer handles immutability):

import { immer } from 'zustand/middleware/immer'

const useStore = create<Store>()(
  immer((set) => ({
    todos: [],
    addTodo: (text) => set((state) => {
      // Mutate directly!
      state.todos.push({ id: Date.now(), text })
    }),
  }))
)

When to use: Complex nested state updates


Combining Middlewares

Order Matters

// ✅ CORRECT: devtools wraps persist
const useStore = create<Store>()(
  devtools(
    persist(
      (set) => ({ /* store */ }),
      { name: 'storage' }
    ),
    { name: 'Store' }
  )
)

// Shows persist actions in DevTools

Common Combinations

// Persist + Devtools
devtools(persist(...), { name: 'Store' })

// Persist + Immer
persist(immer(...), { name: 'storage' })

// All three
devtools(
  persist(
    immer(...),
    { name: 'storage' }
  ),
  { name: 'Store' }
)

Custom Middleware

Logger Example

const logger = (config) => (set, get, api) => {
  return config(
    (...args) => {
      console.log('Before:', get())
      set(...args)
      console.log('After:', get())
    },
    get,
    api
  )
}

const useStore = create(logger((set) => ({ /* store */ })))

TypeScript Logger

import { StateCreator } from 'zustand'

type Logger = <T>(
  f: StateCreator<T, [], []>,
  name?: string
) => StateCreator<T, [], []>

const logger: Logger = (f, name) => (set, get, store) => {
  const loggedSet: typeof set = (...a) => {
    set(...(a as Parameters<typeof set>))
    console.log(`[${name}]:`, get())
  }
  return f(loggedSet, get, store)
}

Middleware API Reference

persist()

persist<T>(
  stateCreator: StateCreator<T>,
  options: {
    name: string                    // Storage key (required)
    storage?: PersistStorage<T>     // Storage engine
    partialize?: (state: T) => Partial<T>  // Select what to persist
    version?: number                // Schema version
    migrate?: (state: any, version: number) => T  // Migration function
    merge?: (persisted: any, current: T) => T     // Custom merge
    onRehydrateStorage?: (state: T) => void       // Hydration callback
  }
)

devtools()

devtools<T>(
  stateCreator: StateCreator<T>,
  options?: {
    name?: string      // Store name in DevTools
    enabled?: boolean  // Enable/disable
  }
)

immer()

immer<T>(
  stateCreator: StateCreator<T>
)

Common Patterns

Reset Store

const initialState = { count: 0 }

const useStore = create<Store>()(
  persist(
    (set) => ({
      ...initialState,
      reset: () => set(initialState),
    }),
    { name: 'storage' }
  )
)

Clear Persisted Data

// Clear localStorage
localStorage.removeItem('storage-name')

// Or programmatically
const useStore = create<Store>()(
  persist(
    (set) => ({
      clearStorage: () => {
        localStorage.removeItem('storage-name')
        set(initialState)
      },
    }),
    { name: 'storage-name' }
  )
)

Official Documentation