Mutable vs immutable objects
By FrontendPro Editorial Team Updated 9/26/2026
Answer
A mutable value can be changed in place; an immutable one cannot — you make a new value instead.
In JavaScript, primitives are immutable (strings, numbers, booleans, symbols, null, undefined) and objects are mutable by default (objects, arrays, maps, dates).
let s = "hello";
s.toUpperCase(); // returns a NEW string
s; // still "hello"
const o = { n: 1 };
o.n = 2; // the object itself changed
Note const does not make anything immutable. It stops you reassigning the binding, not modifying the value it points at.
Making an object immutable
Object.freeze blocks adding, removing and changing properties — but only one level deep:
const config = Object.freeze({ api: "/v1", flags: { beta: true } });
config.api = "/v2"; // ignored (throws in strict mode)
config.flags.beta = false; // works! the nested object is not frozen
A deep freeze means recursing over nested objects yourself.
Working immutably instead
More common than freezing is simply not mutating — produce new values:
const updated = { ...user, role: "admin" };
const added = [...items, newItem];
const removed = items.filter((i) => i.id !== id);
const mapped = items.map((i) => (i.id === id ? { ...i, done: true } : i));
Note which array methods mutate (push, splice, sort, reverse) and which return a copy (map, filter, slice, concat, and the newer toSorted, toReversed).
Why it matters in practice
React and similar libraries decide whether to re-render by comparing references. Mutating state in place leaves the reference identical, so the update is missed — this is the single most common cause of "my component will not re-render". Immutable updates also make changes easier to trace, since no distant code can alter a value you are holding.
