Deep merge plain objects
Recursively merge configuration objects without mutating the inputs and without prototype pollution.
- JavaScript
- GreenSnippets
- MIT
- Executed with Node.js 22
const UNSAFE_KEYS = new Set(["__proto__", "constructor", "prototype"]);
function isPlainObject(value) {
return value !== null && typeof value === "object" && Object.getPrototypeOf(value) === Object.prototype;
}
function deepMerge(...sources) {
const result = {};
for (const source of sources) {
if (!isPlainObject(source)) continue;
for (const [key, value] of Object.entries(source)) {
if (UNSAFE_KEYS.has(key)) continue;
if (isPlainObject(value)) {
result[key] = deepMerge(isPlainObject(result[key]) ? result[key] : {}, value);
} else {
result[key] = value;
}
}
}
return result;
}Example / expected output
deepMerge({ a: { x: 1, y: 2 } }, { a: { y: 3 }, b: 4 }) // { a: { x: 1, y: 3 }, b: 4 }How it works
Spread ({...a, ...b}) only merges one level. This helper merges nested plain objects, lets later sources win, replaces arrays instead of concatenating them, and skips __proto__, constructor and prototype keys so untrusted input cannot pollute Object.prototype.
Related snippets
- TypeScript Tested
Typed Object.keys and Object.entries
Helpers that keep key types instead of widening to string, for objects you know the shape of.
- JavaScript Tested
Pick and omit object keys
Build a new object with only some keys, or without some keys, without mutating the original.
- JavaScript Tested
Deep equality check
Compare two values structurally, including nested objects, arrays, Dates, Maps and Sets.
- JavaScript Tested
Deep clone an object with structuredClone
Copy nested objects, arrays, Maps, Sets and Dates without sharing references, using the built-in structuredClone.

