All questions

Immer produce()

Premium

Immer produce()

Immer's produce(base, recipe) lets you write the next immutable state by mutating a draft, a proxy over the old state, while the original stays untouched and every sub-tree you did not change is shared by reference. You call draft.x = 1, draft.list.push(item), or delete draft.y as if you were editing the state in place, and produce returns a brand-new state that reflects those edits. This is the engine behind Immer, the library that powers immutable updates in Redux Toolkit. See the Immer docs for the full API.

Build immerProduce, an object with a produce method (and a small isDraft helper).

Signature

type Recipe<T> = (draft: T) => void;

const immerProduce: {
  // Run recipe against a draft of baseState, then return the next state.
  // Sub-trees you did not change are kept by reference; if the recipe
  // changed nothing, the original baseState is returned unchanged.
  produce<T>(baseState: T, recipe: Recipe<T>): T;

  // True only for a draft proxy handed to a recipe.
  isDraft(value: unknown): boolean;
};

Examples

const base = { user: { name: 'Ada' }, tags: ['x'] };

const next = immerProduce.produce(base, (draft) => {
  draft.user.name = 'Ada L.';
});

next.user.name;          // 'Ada L.'
base.user.name;          // 'Ada'   (original untouched)
next.tags === base.tags; // true    (unchanged branch shared)
next.user === base.user; // false   (edited branch copied)
const base = { count: 0, list: [1, 2] };

immerProduce.produce(base, () => {}) === base; // true (nothing changed → same reference)

const next = immerProduce.produce(base, (draft) => {
  draft.list.push(3);
});
next.list; // [1, 2, 3]
base.list; // [1, 2]  (original array untouched)

Notes

  • Draft, not a clone — the recipe receives a Proxy over baseState; reading a nested property returns a nested draft lazily, and writing marks that node and its ancestors as copied.
  • Structural sharing — any object or array you did not modify keeps its reference (===) in the result; only nodes on the edited path are shallow-copied.
  • No-op returns the base — if the recipe mutates nothing, produce returns the original baseState object, not a copy.
  • The base is read-only — after produce returns, baseState and all of its nested objects are exactly as they were.
  • The recipe returns nothing — mutate the draft imperatively; returning a replacement value from the recipe is out of scope here (see Going further).
  • Plain objects and arrays only — you do not need to draft Maps, Sets, Dates, or class instances.

FAQ

What is structural sharing?
It means the parts of the state you did not change keep their exact object references in the next state. Only the nodes along the edited path are shallow-copied, so reference-equality checks stay cheap and unchanged sub-trees are reused.
Does produce mutate the original state?
No. The draft is a proxy that copies-on-write into fresh objects, so the base you passed in is left completely untouched. If the recipe changes nothing, produce returns the very same base reference.
Why use a Proxy instead of deep-cloning the state?
Deep-cloning gives every node a new reference and costs O(n) on every update, which defeats reference-equality checks. A Proxy copies only the path you actually touch, so unchanged sub-trees are shared and the work is proportional to the change, not the whole tree.

Unlock the solution & editor

  • Runnable editor + tests

    Solve in the browser with instant Jest feedback.

  • Detailed solutions

    Walkthroughs, edge cases, and complexity notes.

  • Multi-framework variants

    React, Vue, Vanilla, Angular — same question, different stacks.

Upgrade to Premium