Fast, opinionated deep merge utilities for modern node or browser.
npm install fast-deep-mergeimport deepmerge, { mergeAll, deepmergeMutate, mergeStrategies } from 'fast-deep-merge';
const a = { user: { name: 'Ada' }, tags: ['core'] };
const b = { user: { role: 'admin' }, tags: ['fast'] };
const merged = deepmerge(a, b);
// => { user: { name: 'Ada', role: 'admin' }, tags: ['core', 'fast'] }
// Merge many at once
const all = mergeAll(a, b, { flags: { beta: true } });
// Mutating version for maximum speed
deepmergeMutate(a, b);
// Array strategies
mergeStrategies.replaceArrays(a, b);
mergeStrategies.uniqueArrays(a, { tags: ['core', 'ui'] });
mergeStrategies.deepArrays(
{ items: [{ id: 1, meta: { a: 1 } }] },
{ items: [{ id: 1, meta: { b: 2 } }] }
); // call the strategy directly (do not wrap with createMerger)deepmerge(target, ...sources)— clones objects/arrays and returns a new result.deepmergeWithOptions(target, ...sources, options)— same asdeepmergewith custom behavior.deepmergeMutate(target, ...sources)— mutatestargetfor the fastest path.mergeAll(...objects)— convenience wrapper overdeepmerge.createMerger(options)— returns a preconfigured merge function.mergeStrategies— presets:replaceArrays,uniqueArrays,deepArrays.
type MergeOptions = {
clone?: boolean; // default true
arrayMerge?: (target: any[], source: any[], options?: MergeOptions) => any[];
isMergeableObject?: (value: any) => boolean; // default: plain object only
};clone: falselets the merge reuse references (faster, but be aware of mutation).arrayMergecustomizes array handling (e.g., replace, concat, unique, deep element merge).isMergeableObjectcontrols what gets merged vs. overwritten.
- Prefer
deepmergeMutatewhen you own the target object and can mutate it. - Avoid passing non-plain objects unless you provide a custom
isMergeableObject. - For very large arrays, prefer
replaceArraysto skip concatenation.
Types are published via index.d.ts and map 1:1 to the runtime API.
Run the benchmark suite to compare performance with deepmerge:
npm install
npm run benchBenchmarking results:
🚀 fast-deep-merge vs deepmerge
============================================================
📊 Test 1: Shallow merge (15 keys - typical config/API response)
fast-deep-merge 7.30ms 1,370,630 ops/sec
deepmerge 14.00ms 714,094 ops/sec
Speedup: 1.92x faster
📊 Test 2: Deep nested merge (depth: 3, width: 4 - typical nested config)
fast-deep-merge 37.09ms 134,797 ops/sec
deepmerge 506.59ms 9,870 ops/sec
Speedup: 13.66x faster
📊 Test 3: Medium arrays (50 elements each - typical list/collection)
fast-deep-merge 5.09ms 982,519 ops/sec
deepmerge 156.79ms 31,891 ops/sec
Speedup: 30.80x faster
📊 Test 4: Multiple sources (3 objects - typical config override chain)
fast-deep-merge 13.51ms 222,001 ops/sec
deepmerge 89.47ms 33,530 ops/sec
Speedup: 6.62x faster
Performance Summary: fast-deep-merge is ~2x to 31x faster than deepmerge across all tested scenarios, with the largest gains on array-heavy and deeply nested structures.
Results vary by Node.js version and hardware. For best performance, use Node.js 18+.
- Plain objects: merged recursively, cloning by default (matches
deepmerge). - Arrays (default): concatenated (matches
deepmergedefault). - Multiple sources:
mergeAll(...objs)behaves likedeepmerge.all. - Non-plain objects (Date, RegExp, Map, Set): treated as values; source overwrites target and is cloned to avoid mutation (safer;
deepmergekeeps the source reference). - Functions: treated as values; source overwrites target (reference preserved).
- Null / undefined: overwritten by source (matches
deepmerge). - Primitive → object: source wins (matches
deepmerge). - Array presets:
mergeStrategies.replaceArrays: overwrite arrays.mergeStrategies.uniqueArrays: concat + dedupe primitives (same asdeepmergewith a customarrayMerge).mergeStrategies.deepArrays: deep-merge array elements by index (differs fromdeepmerge, which concatenates).
- Mutation path:
deepmergeMutatemerges in-place for maximum speed;deepmergeis non-mutating and clones internally. - Custom behavior: use
deepmergeWithOptionsorcreateMergerto providearrayMerge/isMergeableObject(e.g., treat Maps/Sets as mergeable, or to align withdeepmergesemantics).
- Plain objects/arrays are merged by default (like
deepmerge). - Non-plain objects (Date, RegExp, Map, Set, etc.) are treated as values: the source overwrites the target, but they are cloned to avoid mutations.
- If you need custom mergeable logic (e.g., merge Maps/Sets differently), pass
isMergeableObjectandarrayMergeintodeepmergeWithOptionsorcreateMerger.
MIT