An array modified in one part of the code can shift under the feet of another part that was already holding it. The bug is silent, shows up far from its cause, and survives careful reading.
Immutability cuts that scenario short with a simple discipline: nothing is modified, a new value is produced.
Definition
Immutability is the principle that a value, once created, is never modified again. Every change goes through creating a copy that carries it. The old version stays intact for whoever was holding it.
const cart = ["keyboard", "mouse"];
const withScreen = [...cart, "screen"];
console.log(cart); // [ 'keyboard', 'mouse' ]
console.log(withScreen); // [ 'keyboard', 'mouse', 'screen' ]
const profile = { name: "Ada", city: "London" };
const moved = { ...profile, city: "Paris" };
console.log(profile.city); // London
console.log(moved.city); // ParisSpread (...) does all the work: it copies the contents into a fresh structure, and whatever is written after it replaces what was just copied.
The same move, in an immutable version
Every operation that changes things in place has a counterpart that returns a copy. This three-column table covers the four everyday cases.
| Need | Version that mutates | Version that copies |
|---|---|---|
| Append at the end | list.push(x) | [...list, x] |
| Remove an item | list.splice(i, 1) | list.filter(...) |
| Sort | list.sort() | list.toSorted() |
| Change a field | obj.field = v | { ...obj, field: v } |
Object.freeze and its limit
const prevents reassigning a name, not modifying what it holds. Object.freeze() goes further, but stops at the first level.
const config = Object.freeze({ lang: "en", options: { sound: true } });
console.log(Object.isFrozen(config)); // true
console.log(Object.isFrozen(config.options)); // false, freezing is shallow
config.options.sound = false;
console.log(config.options.sound); // falseWriting config.lang = "fr" is silently ignored in a classic script, and throws a TypeError in a module. Freezing protects, but not the same way everywhere.
Frequently asked questions
Is copying all the time expensive?
A shallow copy only duplicates the first level: nested values are shared, not recreated. On lists of a few thousand items, the cost is nothing compared to the hours spent chasing an unexpected Mutation. The real risk would be systematic deep copying, which is rarely needed.
Does const make a value immutable?
No, and that is the most widespread confusion. const forbids reassigning the name, nothing more. An array declared with const happily accepts push(), because the variable still points at the same array. See Reference vs value for the details.
Why do modern interfaces care so much about it?
Because a rendering library detects a change by comparing two references. If the object was modified in place, the old and the new one are identical and nothing refreshes. Our React course comes back to this from the very first state update, where the rule becomes concrete.