immutability-helper
A drop-in replacement for react-addons-update that mutates a copy of your data without touching the original, using a concise MongoDB-inspired command syntax.
Repository Health
Technical Analysis
immutability-helper gives JavaScript and TypeScript projects a small, focused API for producing updated copies of deeply nested data structures without hand-writing chains of Object.assign or spread operators. Instead of manually cloning every level of a nested object to change one field, you describe the change with a spec object built from $-prefixed commands ($set, $push, $merge, $splice, and more), and the library figures out which parts of the structure actually need to be copied.
The library grew out of Facebook’s original react-addons-update and keeps that same command syntax, which makes it a near drop-in replacement for anyone migrating off the deprecated React addon. It preserves referential equality wherever nothing changed, which matters for React’s shouldComponentUpdate and other reference-equality-based optimizations — unaffected branches of your object tree keep their original references so consumers can skip re-rendering.
Beyond the built-in commands, it exposes an extend() function and a Context class so teams can register custom commands or maintain an isolated command registry per module, rather than mutating a single global instance. The whole implementation lives in one TypeScript file with a fully generic type system that infers the correct spec shape (array spec, map spec, set spec, or object spec) from the target’s type, giving accurate autocomplete and compile-time checks for update specs on arbitrarily nested data.
What You Get
- A concise
update(target, spec)function supporting$set,$push,$unshift,$splice,$merge,$unset,$toggle,$apply,$add, and$removecommands - Referential-equality preservation — object branches unaffected by an update keep their original object reference
- A
Contextclass for creating an isolated command registry, so custom commands in one module don’t leak into another’s - An
extend()API for registering project-specific commands (e.g.$addtax) alongside the built-in ones - Full TypeScript definitions that infer the correct spec shape (array, Map, Set, or object) from the target type, including support for custom command types via
CustomCommands<T> - First-class support for updating
MapandSetvalues via$add/$remove, not just plain objects and arrays
Common Use Cases
- Updating deeply nested React component state without writing manual
Object.assignchains for each level - Writing Redux or Redux Toolkit reducers that need to return new state objects while preserving unaffected slices
- Implementing
shouldComponentUpdateorReact.memocomparisons that rely on reference equality to skip re-renders - Migrating legacy codebases off the deprecated
react-addons-updatepackage with minimal syntax changes - Manipulating collections (arrays, Maps, Sets) immutably in any state-management layer, not just React
Under The Hood
Architecture
The entire library is implemented in a single index.ts file structured around one exported Context class: a commands map seeded with the built-in $-prefixed handlers, and an update(object, spec) method that walks the spec’s keys, dispatches to a matching command function when the key starts with $, or recurses into update() for the corresponding nested value otherwise. A module-level defaultContext instance backs the default update/extend/isEquals exports, while consumers who need isolated custom commands can instantiate their own Context. The core invariant the architecture protects is copy-on-write: a nextObject === object check gates every mutation path, so a shallow copy() (via Object.assign, new Map(...), or new Set(...) depending on target type) is only made the first time a branch actually changes, letting unaffected object references pass through untouched.
Tech Stack
The package is authored directly in TypeScript (index.ts) and compiled to index.js plus index.d.ts via tsc per its package.json build script, with no runtime dependencies at all — Object.assign, Object.getOwnPropertySymbols, and native Map/Set do all the heavy lifting, with small polyfill-style fallbacks (assign, getAllKeys) for older environments lacking those APIs. Development tooling is jest with ts-jest for running the TypeScript test suite directly, tslint for linting (via tslint.json), and rimraf for cleaning build artifacts between steps; there is no bundler since the package ships a single compiled file plus type declarations.
Code Quality
The test suite (test.ts, over 600 lines) exercises every built-in command, custom extend() behavior, Context isolation, and edge cases like frozen source objects and invalid specs, and the jest config enforces a 100% coverage threshold across branches, functions, lines, and statements — meaning the CI-gated npm test script fails outright if any code path goes untested. Error handling is explicit: an internal invariant() helper throws descriptive, developer-facing messages (e.g. “expected target of $push to be an array”) for malformed specs rather than silently producing wrong output, and those messages are stripped to a generic “Invariant failed” in production builds to save bundle size. TypeScript’s structural generics are used throughout Spec<T, C> to make invalid spec shapes a compile error rather than a runtime surprise.
What Makes It Unique
Rather than a general deep-clone or a full immutable-collection library like Immutable.js, immutability-helper’s whole value proposition is a minimal, declarative diffing/copy strategy expressed as a plain object spec, which stays close to the shape of the data being updated and avoids the API-conversion cost of adopting a whole new data structure family. Its extensibility model — a per-instance Context plus a global extend() — lets teams add domain-specific update commands without forking the library, a pattern most comparable spread/merge helper libraries don’t offer.
Used by 3 apps in this directory
ChartBrew
Analytics · Databases
Open-source reporting platform to build live dashboards from SQL, NoSQL, APIs, and SaaS tools with an AI assistant that creates charts from natural language.
Plasmic
CMS · Low Code Platforms · No Code Platforms
The open-source visual builder that lets teams design React apps and websites with drag-and-drop while integrating seamlessly with your codebase.
ToolJet
Low Code Platforms · No Code Platforms · AI Agents
Open-source AI-native platform to build and deploy internal tools, workflows, and AI agents with a visual drag-and-drop builder and 80+ data source integrations.