Skip to content
masterclassBuddyTech Buzz5 fresh blogs · 5:00 AM IST

Zustand · Const

middleware composition

Zustand middleware is composed by nesting state-creator wrappers, such as persist inside devtools, and passing the result directly to create. This lets one store combine persistence, debugging, reducers, Immer updates, or other documented extensions.

How it works

Middleware composition wraps one state creator with another, allowing several independent store enhancements to be applied together. Put the composed wrappers immediately inside create so TypeScript can infer the contextual types correctly; the documentation demonstrates nesting persist inside devtools.

Syntax

create<State>()(middlewareA(middlewareB(stateCreator, options), options))

Returns

A composed state creator passed to create; the wrapped middleware behaviors operate on the resulting Zustand store.

Key points

  • Zustand middleware is designed as composable functions that extend a store's behavior.
  • The devtools middleware adds Redux DevTools integration for inspecting and time-travel debugging state changes.
  • The persist middleware stores state in localStorage or another configured storage engine.
  • A store assembled from slices accepts middleware composition through the same wrapping pattern as a regular store.

Common mistakes

  • For a store assembled from slices, apply middleware at the combined store level rather than separately inside individual slices.

Example: Composing Zustand middleware

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

type BearState = {
  bears: number
  increase: () => void
}

export const useBearStore = create<BearState>()(
  devtools(
    persist(
      (set) => ({
        bears: 0,
        increase: () => set((state) => ({ bears: state.bears + 1 })),
      }),
      { name: 'bear-store' },
    ),
  ),
)

The store creator is wrapped first with persist and then with devtools, composing both behaviors directly inside create.

When to use it

Persistence plus Redux DevTools

A browser store must survive reloads while remaining inspectable during development.

Middleware around a combined store

A large store is assembled from multiple independently defined slices.

Reducer and action integration

An application uses reducer-style updates alongside Zustand's store API.

Immer-enhanced updates

State updates should use convenient mutable syntax while retaining immutable behavior.

Interview questions

QHow do you compose multiple Zustand middleware functions?

Middleware is composed by nesting state-creator wrappers, such as devtools(persist(stateCreator, options)). The resulting creator is passed directly to create, so both middleware layers affect the same store.

Follow-up: How should middleware be applied to a sliced store? Apply it once around the combined store creator, not independently inside each slice, because slice-level application can cause unexpected behavior.

QWhy is middleware placed immediately inside create in TypeScript?

The documented TypeScript pattern is create<State>()(middleware(stateCreator)). The extra invocation preserves the explicit state annotation while allowing the middleware wrappers to receive the creator in context.

Follow-up: When might the curried form be unnecessary? Middleware such as combine creates state and can infer it, so the non-curried create(combine(...)) form is suitable.

QHow should middleware be used with a store built from slices?

A combined store can be wrapped with middleware in the same way as an ordinary store. For example, persist can wrap the creator that spreads multiple slice creators, with its storage options supplied alongside the creator.

Follow-up: What is the principal design rule for slices? Keep middleware in the combined root store rather than embedding it in individual slice creators.

  • persist
  • devtools
  • redux
  • immer
  • combine
  • create

Source: Reference - Zustand Docs. Checked 2026-10-15.