Immer
By Michel Weststrate
Immer is a JavaScript library for working with immutable state by letting developers write code that appears to mutate a draft object directly, while producing a new, structurally shared immutable result behind the scenes. It is commonly…
Definition
Immer is a JavaScript library for working with immutable state by letting developers write code that appears to mutate a draft object directly, while producing a new, structurally shared immutable result behind the scenes. It is commonly used in state management contexts such as Redux reducers and React state updates, where immutability is required but deeply nested manual copying is error-prone and verbose.
Overview
Immer addresses a common source of bugs in JavaScript applications that rely on immutable state: producing a new object that reflects a change to a deeply nested property without mutating the original, typically requires manually spreading every intermediate level of the object tree, which is tedious and easy to get wrong. Immer lets developers write what looks like ordinary mutating code against a draft, then handles the immutable copying automatically. Mechanically, Immer's core function, produce, accepts a base state and a recipe function. Inside that recipe, the developer receives a draft, which is a Proxy-wrapped version of the base state that records reads and writes without touching the original object. When a property on the draft is assigned, Immer marks that part of the tree as modified and, once the recipe finishes, walks the recorded changes to construct a new object where only the modified branches are recreated; unmodified branches are shared by reference with the original, a technique known as structural sharing. This proxy-based approach means array methods, nested object mutation, and even delete operations behave as expected inside the recipe while still yielding an immutable result outside it. Immer differs from state-management libraries themselves; it does not manage state or dispatch actions, but is instead a utility that plugs into reducers or state-update functions written for tools like Redux, useState, or Zustand. It differs from libraries like Immutable.js, which introduce entirely new persistent data structures (their own Map and List types) that must be converted to and from plain objects, whereas Immer works directly with native JavaScript objects and arrays, requiring no data-type migration. In practice, Immer became especially popular after Redux Toolkit adopted it internally for writing reducers, since it eliminates the boilerplate of spread-heavy reducer logic while preserving Redux's requirement that state updates be immutable and traceable. Beyond Redux, developers use Immer directly inside custom React hooks, Zustand stores, or any function that needs to safely update deeply nested state without hand-rolled copying, and its patches feature can even record and replay changes for undo/redo functionality. The trade-offs of using Immer include a small runtime performance cost from Proxy interception, which is negligible for typical UI state but can matter in extremely hot code paths or with very large state trees, and a learning curve around understanding what does and does not trigger draft tracking, particularly with non-plain objects like class instances or Map and Set unless the mapSet plugin is enabled. Developers should also avoid returning a new value from the recipe while also mutating the draft, since combining both patterns produces undefined behavior; picking one approach consistently avoids this pitfall.
Key Features
- Provides a produce function for writing mutation-style state updates
- Uses JavaScript Proxies to track reads and writes on a draft
- Generates immutable results via structural sharing for efficiency
- Integrates directly with Redux, Redux Toolkit, and Zustand reducers
- Works with plain objects and arrays without new data structures
- Supports an optional patches feature for recording state changes
- Offers a mapSet plugin for immutable updates to Map and Set
- Requires no schema or class wrapping around existing state