immutable-js
Persistent immutable List, Map, Set, and Stack collections for JavaScript with structural sharing.
Repository Health
Technical Analysis
Immutable.js provides a set of persistent, immutable collection types for JavaScript — List, Map, OrderedMap, Set, OrderedSet, Stack, and Record — that mirror the native Array/Map/Set APIs while never mutating in place. Every write operation (set, push, delete, merge) returns a new collection instead of modifying the original, which makes state changes trivially detectable with reference equality (===) rather than deep comparison.
Under the hood, collections are implemented as hash-array-mapped tries (HAMTs) and vector tries, the same structural-sharing technique popularized by Clojure and Scala. This lets Immutable.js “copy” a collection of any size in O(log32 n) by sharing untouched branches of the trie between the old and new versions, instead of doing a full deep copy on every update. The library also ships a lazy Seq type that chains operations like map and filter without allocating intermediate collections, only evaluating when a terminal operation forces it.
With zero runtime dependencies, first-class TypeScript and Flow type definitions, and 33k+ GitHub stars, it remains one of the most widely used building blocks for predictable state management in React/Flux-style applications and anywhere reference-equality change detection matters.
What You Get
- A full family of persistent collection types (List, Map, OrderedMap, Set, OrderedSet, Stack, Record) with APIs that closely mirror native Array/Map/Set
- Structural sharing via hash-array-mapped tries and vector tries, so updates are O(log32 n) instead of full copies
- Value equality via
Immutable.is()/.equals(), plus reference-equality shortcuts (===) when an update produces an identical result - A lazy
Seqtype for chainingmap/filter/etc. without allocating intermediate collections - A functional read/write API (
get,getIn,setIn,updateIn,merge,mergeDeep) for working with plain JS values interchangeably with Immutable collections - First-class TypeScript and Flow type definitions bundled with the npm package
withMutations/transient batching to apply many changes efficiently before producing a single new immutable result
Common Use Cases
- Modeling application state in React/Flux/Redux-style architectures where reference-equality change detection drives re-renders
- Building undo/redo stacks or time-travel debugging where prior states must remain untouched and cheap to retain
- Safely sharing nested data across modules or workers without defensive copying to prevent accidental mutation
- Memoizing expensive computations keyed on collection identity, since unchanged updates return the same reference
- Composing large nested records/maps (e.g. normalized API responses) with deep
getIn/setIn/mergeDeephelpers
Under The Hood
Architecture
The public API in src/Immutable.js re-exports a family of concrete collection types (List, Map, OrderedMap, Stack, Set, OrderedSet, Record, Seq) that all extend a shared Collection/CollectionImpl base implementing the iteration, equality, and transformation protocol once. Each concrete type (e.g. List.js, Map.js) layers its own persistent-trie storage on top of that shared base, so operations like map, filter, and reduce are implemented centrally in CollectionImpl.js/Operations.js while structural details (index-based vector tries for List, hashed tries for Map/Set) live in the type-specific files. Cross-cutting concerns are pulled into dedicated modules — TrieUtils.ts for shared trie constants and ref helpers, Hash.ts for value hashing, predicates/ for type guards (isList, isMap, etc.), and functional/ for the standalone get/setIn/merge-style functions that operate on both Immutable and plain JS values. This separation means adding a new collection type mostly means implementing trie storage plus reusing the shared iteration/equality machinery, and changing the core trie strategy would ripple through every concrete collection type.
Tech Stack
The library is authored in a mix of TypeScript (type definitions, utilities like Hash.ts/TrieUtils.ts) and JavaScript (the performance-sensitive collection implementations such as List.js, Map.js, Operations.js), built with Rollup (resources/rollup-config.mjs) into CommonJS and ES module bundles plus a bundled .d.ts. It has zero runtime dependencies — devDependencies cover TypeScript, Flow, ESLint 9 with typescript-eslint, Prettier, Jest for unit tests, tstyche for type-level testing, fast-check for property-based testing, and a Next.js-based documentation website in website/. Package size is actively budgeted via size-limit against dist/immutable.es.js.
Code Quality
Testing is extensive: 47+ files under __tests__/ (Jest) cover individual collection types (List.ts, Map.ts, OrderedMap.ts, Record.ts), cross-cutting concerns (Equality.ts, Conversion.ts, hash.ts), and property-based tests via fast-check, plus tstyche type tests for the TypeScript definitions and Flow’s own test suite under type-definitions/flow-tests. CI (.github/workflows/ci.yml) runs separate lint, type-check, and build-and-test jobs with a check-git-clean guard to catch generated-file drift. Code style is enforced via ESLint (flat config, typescript-eslint) and Prettier with lint/format npm scripts wired into CI. The mix of untyped legacy .js collection files alongside newer .ts modules shows an incremental TypeScript migration in progress rather than a from-scratch typed codebase.
What Makes It Unique
Immutable.js’s distinguishing technical choice is its use of hash-array-mapped tries and vector tries for structural sharing — the same approach used by Clojure’s and Scala’s persistent collections — rather than the simpler copy-on-write array cloning many “immutable helper” libraries use. This gives near-O(1) amortized updates on large collections instead of O(n) copies, while still guaranteeing that untouched references remain byte-for-byte identical (enabling === shortcuts). Pairing that with a lazy Seq abstraction for chained operations, and a functional API layer that treats plain objects and Immutable collections uniformly, sets it apart from lighter-weight alternatives that only shallow-freeze or diff plain objects.
Used by 7 apps in this directory
Bun
Developer Tools
An all-in-one JavaScript and TypeScript toolkit — one Rust-and-JavaScriptCore binary that replaces Node.js, npm, a bundler, and a test runner with faster equivalents.
Grafana
Monitoring · Analytics
The open-source observability platform that unifies metrics, logs, and traces from any data source into dynamic, queryable dashboards.
Mastodon
Social Media
Run your own federated social network on the open ActivityPub standard with no ads, no algorithms, and no corporate control over your community.
MLflow
AI Development · Monitoring
The open source AI engineering platform for debugging, evaluating, monitoring, and optimizing production LLMs and agents at scale.
Penpot
Design Tools
Open-source design platform with GPU-accelerated canvas, native design tokens, and MCP-powered AI workflows for teams that ship fast.
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.
swagger-ui
Developer Tools
Transform OpenAPI specifications into interactive, browser-based API documentation that developers and consumers can explore and test live.