Combine Immer with DevTools
A store uses Immer for concise immutable updates and Redux DevTools for inspection.
Zustand · Const
Zustand middleware is nested, and devtools should usually be the outermost wrapper. With Immer, use devtools(immer(...)) so DevTools is applied last and other middleware cannot mutate setState first.
Middleware composition is nested: the outer middleware is applied last around the inner ones. Zustand recommends putting devtools as the outermost wrapper so other middleware cannot mutate setState after DevTools has configured it.
devtools(immer((set) => ({ ... })))devtools at the outer edge of the middleware composition whenever possible.devtools(immer(...)).setState before DevTools receives it.create so TypeScript can infer the contextual types.devtools inside another middleware can produce the wrong composition order; wrap the other middleware with devtools instead.immer combination, use devtools(immer(...)), not the reverse nesting.import { create } from 'zustand'
import { devtools, immer } from 'zustand/middleware'
type BearState = {
bears: number
increase: (by: number) => void
}
const useBearStore = create<BearState>()(
devtools(
immer((set) => ({
bears: 0,
increase: (by) =>
set((state) => {
state.bears += by
}),
})),
),
)
useBearStore.getState().increase(2)
console.log(useBearStore.getState().bears)Place devtools outside immer so it is applied last in the middleware composition.
A store uses Immer for concise immutable updates and Redux DevTools for inspection.
Several middleware functions wrap one store creator and their application order affects behavior.
A TypeScript store needs contextual inference while middleware is composed.
Middleware is composed by nesting state creators. The recommended arrangement puts devtools on the outside, making it the last middleware applied and preserving its control over setState.
Follow-up: What ordering should be used with immer? Use devtools(immer(...)), because devtools should be applied last.
devtools and immer nesting?Use devtools(immer(...)), rather than immer(devtools(...)). The outer devtools wrapper ensures the DevTools middleware is applied last.
Follow-up: Why does the order matter? Other middleware may mutate setState; placing DevTools last prevents that mutation from occurring before DevTools observes it.
devtools last?DevTools should be placed at the end of the middleware chain. This means it wraps the other middleware rather than being wrapped by it.
Follow-up: Does combine require special middleware mutator handling? No; the documentation says combine does not mutate the store.
devtoolsimmerpersistsubscribeWithSelectorcombinecreate