Suggest an editImprove this articleRefine the answer for “What does the immer middleware do?”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**`immer`** is one of the most useful middlewares in **Zustand**, and it makes working with state simpler, safer, and more convenient, especially if you don't want to manually track immutability. **Key point:** the `immer` middleware lets you change state "as if directly", but under the hood Zustand still creates an immutable copy (it does not mutate the original).Shown above the full answer for quick recall.Answer (EN)Image`immer` is one of the most useful middlewares in **Zustand**, and it makes working with state **simpler, safer, and more convenient**, especially if you don't want to manually track immutability. --- ## The essence in one sentence > The `immer`**-middleware** lets you **change state "as if directly"**, but under the hood Zustand still creates an **immutable copy** (it does not mutate the original). --- ## Without `immer` In plain Zustand you have to **explicitly return a new object** so React understands that the state changed: ```javascript import { create } from 'zustand' const useTodoStore = create((set) => ({ todos: [], addTodo: (text) => set((state) => ({ todos: [...state.todos, { text, done: false }], })), })) ``` Here it's important **not to mutate** `state.todos`, otherwise React won't see the change. That is, you cannot do `state.todos.push(...)`. --- ## With `immer` With `immer` you can **write code in a mutable style**, and the library creates an immutable copy under the hood: ```javascript import { create } from 'zustand' import { immer } from 'zustand/middleware/immer' const useTodoStore = create( immer((set) => ({ todos: [], addTodo: (text) => set((state) => { state.todos.push({ text, done: false }) // mutation is fine! }), toggleTodo: (index) => set((state) => { state.todos[index].done = !state.todos[index].done }), })) ) ``` Now Zustand automatically: - creates a "draft" of the state (`draft`); - applies your changes to it; - creates a **new version** of the state with no mutations. --- ## How this works under the hood `immer` uses the Immer library, which implements the **immutable state via Proxy** concept. Roughly like this: 1. Zustand creates a proxy object (`draft`) of the state; 2. You "mutate" it like a regular object; 3. Immer records the changes and creates a **new immutable copy** of the state. --- ## Advantages | Advantage | Description | |---|---| | Clean code | You write in a natural style without copying arrays and objects | | Safety | Guarantees immutability (the original is not mutated) | | Simplicity | No need to manually use spreads (`...`) | | Compatibility | Works with `persist`, `devtools`, and other middleware | --- ## Important to remember - Immer is a bit **slower** than "pure" immutable updates if updates are very frequent (for example, 60 times per second in an animation). But in typical applications the difference is unnoticeable. - Immer **cannot be used together with direct mutations outside** `set()` - it only works inside the `set((state) => { ... })` function. --- ## Summary | Parameter | Without immer | With immer | |---|---|---| | Updating an array | `set({ todos: [...state.todos, newTodo] })` | `set((s) => { s.todos.push(newTodo) })` | | Immutability | manual | automatic | | Code | verbose | concise | | Safety | depends on the developer | guaranteed by Immer |For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.