Overview

Immer is a small, open-source JavaScript library designed to simplify immutable state management. It provides a way to work with immutable data structures using a familiar, mutable syntax, abstracting away the complexities of manual deep cloning or spread operations. This approach is particularly valuable in applications where state predictability is crucial, such as those built with React or Redux, where direct state mutation can lead to hard-to-debug side effects and performance issues due to inefficient re-renders or incorrect change detection.

The core mechanism of Immer is its "copy-on-write" strategy. When a developer uses Immer's produce function, they receive a "draft" version of their state. Changes made to this draft are recorded, and once the function completes, Immer intelligently applies these changes to the original immutable state, producing a new immutable state object with only the necessary parts copied. This ensures that the original state remains untouched, preserving immutability, while offering a more ergonomic development experience than traditional immutable updates.

Immer is suitable for developers who need to manage complex, nested state objects without sacrificing performance or readability. It shines in scenarios where state updates involve modifying properties deep within an object tree. For instance, updating an item in a nested array within a Redux reducer often requires extensive spread operators (...) or manual cloning, which can become verbose and error-prone. Immer streamlines these operations, allowing developers to write code that looks like a direct mutation, such as draft.todos[0].text = 'Buy groceries', while maintaining immutable principles under the hood.

While often associated with React and Redux, Immer is a general-purpose utility library that can be used with any JavaScript application that benefits from immutable data patterns. Its small footprint and performance optimizations make it a practical choice for a wide range of projects, from small components to large-scale applications. Developers transitioning from mutable state patterns often find Immer's API intuitive, bridging the gap between familiar imperative coding styles and the requirements of functional, immutable state management. The library's adoption within the Redux Toolkit, for example, demonstrates its utility in simplifying state logic in complex applications, as detailed in the Redux Immer usage guide.

Key features

  • Mutable-like syntax: Write state updates using direct assignment and array methods (e.g., push, splice) on a "draft" object, which is then translated into an immutable update.
  • Copy-on-write mechanism: Immer automatically detects changes made to the draft and produces a new immutable state, copying only the modified parts of the state tree.
  • Automatic structural sharing: Unchanged parts of the state tree are reused between the old and new states, optimizing memory usage and performance.
  • TypeScript support: Provides strong type inference for drafts and produced states, improving developer experience and catching errors at compile time.
  • Integration with state management libraries: Seamlessly works with Redux, Zustand, Vuex, and other state management solutions to simplify reducer logic.
  • Patch generation: Can generate "patches" that describe the changes between two states, useful for debugging, undo/redo functionality, or sending minimal updates over a network.
  • Small bundle size: Designed to be lightweight, adding minimal overhead to application bundles.

Pricing

Immer is an entirely free and open-source library.

Feature Availability Notes
Core Library Free All features included
Support Community Via GitHub issues and discussions

Pricing as of 2026-05-28. For the most current information, refer to the Immer documentation.

Common integrations

  • React: Often used with React's useState or useReducer hooks to manage complex component-local state immutably.
  • Redux: Significantly simplifies Redux reducer logic, especially when dealing with nested state updates, as demonstrated in the Redux documentation on Immer usage.
  • Redux Toolkit: Integrated directly into Redux Toolkit's createSlice and createReducer functions, making it the default for writing mutable-looking immutable updates.
  • Vuex: Can be used within Vuex mutations or actions to manage state in Vue.js applications.
  • Zustand: Compatible with Zustand, offering an alternative way to manage state immutably within a Zustand store.
  • MobX: While MobX often encourages mutable state, Immer can still be used to produce immutable snapshots or process updates before MobX observes them.

Alternatives

  • Immutable.js: A library offering a collection of persistent immutable data structures, such as List, Map, and Set, that perform structural sharing. It requires adopting its specific data structures throughout the application.
  • Ramda.js: A functional programming utility library for JavaScript that provides immutable-friendly functions for data manipulation, emphasizing a point-free style.
  • Lodash/fp: The functional programming variant of Lodash, providing auto-curried and immutable-first functions for common data transformations.

Getting started

To begin using Immer, first install it via npm or yarn:

npm install immer
# or
yarn add immer

Then, you can use the produce function to create new immutable states from existing ones by modifying a draft:

import { produce } from 'immer';

const baseState = {
  user: {
    id: 1,
    name: 'Alice',
    todos: [
      { id: 'a', text: 'Learn Immer', done: true },
      { id: 'b', text: 'Build a project', done: false }
    ],
  },
  settings: {
    theme: 'light'
  }
};

// Example 1: Updating a nested property
const nextState1 = produce(baseState, draft => {
  draft.user.todos[1].done = true;
});

console.log('Original state unchanged:', baseState.user.todos[1].done === false);
console.log('New state updated:', nextState1.user.todos[1].done === true);

// Example 2: Adding an item to an array
const nextState2 = produce(baseState, draft => {
  draft.user.todos.push({ id: 'c', text: 'Deploy to production', done: false });
});

console.log('Original todos count:', baseState.user.todos.length);
console.log('New todos count:', nextState2.user.todos.length);

// Example 3: Modifying a top-level property
const nextState3 = produce(baseState, draft => {
  draft.settings.theme = 'dark';
});

console.log('Original theme:', baseState.settings.theme);
console.log('New theme:', nextState3.settings.theme);

This example demonstrates how Immer allows direct modifications to the draft object, which then results in a new immutable nextState without altering the original baseState. The console logs verify that the original state remains unchanged, while the new state reflects the desired modifications.