Lodash _.mergeWith() method

Intermediate
⏱️ 9 min read
📚 Updated: May 2026
🎯 3 Code examples
🚀 3 Try-it labs
Lodash

What you’ll learn

  • How _.mergeWith(object, ...sources, customizer) extends _.merge().
  • How to concatenate arrays while keeping Lodash’s normal recursive object merge.
  • Why returning undefined from the customizer is the fallback signal.
  • How to write field-specific rules without accidentally breaking nested merges.

Prerequisites

You should already understand _.merge(): it is recursive, it mutates the destination, and its default array behavior is positional by index.

  • Same mutation contract: the first argument is still modified and returned.
  • Customizer is selective: use it only where default _.merge() rules are not enough.
  • undefined means fallback: returning it tells Lodash to continue with normal deep merge.

Overview

_.mergeWith behaves like _.merge, but before Lodash chooses the merged value it asks your customizer. If your customizer returns a value, that value wins. If it returns undefined, Lodash uses the normal recursive merge behavior for that property.

Customize only conflicts

Great for arrays, counters, special keys, and domain-specific merge rules.

Mutates & returns

Same as _.merge: the destination absorbs the result.

Fallback via undefined

Returning undefined is intentional, not a mistake.

Syntax

javascript
_.mergeWith(object, [...sources], customizer)
  • object: destination object. It is mutated and returned.
  • sources: one or more source objects merged left to right.
  • customizer: invoked as (objValue, srcValue, key, object, source, stack).
  • Return rule: return a custom value to override, or undefined for Lodash’s default merge.
1

Concatenate arrays during a deep merge

The most common _.mergeWith use case is replacing _.merge’s positional array behavior with concatenation.

javascript
import mergeWith from "lodash/mergeWith";

const defaults = {
  modules: ["core", "auth"],
  theme: { colors: ["blue"], mode: "light" }
};

const project = {
  modules: ["payments"],
  theme: { colors: ["green"] }
};

const result = mergeWith({}, defaults, project, (objValue, srcValue) => {
  if (Array.isArray(objValue)) {
    return objValue.concat(srcValue);
  }
  return undefined; // keep Lodash's normal deep merge
});

// -> {
//      modules: ["core", "auth", "payments"],
//      theme: { colors: ["blue", "green"], mode: "light" }
//    }
Try it Yourself
2

Use field-specific merge rules

The customizer receives the current key, so you can treat specific fields differently while letting everything else merge normally.

javascript
import mergeWith from "lodash/mergeWith";

const current = {
  stats: { views: 10, likes: 2 },
  tags: ["lodash"],
  status: "draft"
};

const patch = {
  stats: { views: 5, likes: 4 },
  tags: ["objects"],
  status: "published"
};

const merged = mergeWith({}, current, patch, (objValue, srcValue, key) => {
  if (key === "views" || key === "likes") {
    return (objValue || 0) + (srcValue || 0);
  }
  if (key === "tags" && Array.isArray(objValue)) {
    return Array.from(new Set(objValue.concat(srcValue)));
  }
  return undefined;
});

// -> {
//      stats: { views: 15, likes: 6 },
//      tags: ["lodash", "objects"],
//      status: "published"
//    }
Try it Yourself
3

The undefined fallback rule

A customizer that always returns something disables normal deep merging for those values. Return undefined for values you do not want to customize.

javascript
import mergeWith from "lodash/mergeWith";

const left = {
  options: { theme: "light", pageSize: 20 },
  roles: ["reader"]
};

const right = {
  options: { pageSize: 50 },
  roles: ["admin"]
};

const safe = mergeWith({}, left, right, (objValue, srcValue) => {
  if (Array.isArray(objValue)) {
    return objValue.concat(srcValue);
  }
  return undefined;
});

// -> {
//      options: { theme: "light", pageSize: 50 },
//      roles: ["reader", "admin"]
//    }
Try it Yourself

📋 _.mergeWith vs related operations

Topic_.mergeWith_.merge_.assignWithManual reducer
DepthRecursiveRecursiveShallowYou decide
CustomizerYesNoYesManual
Mutates first argYesYesYesDepends on implementation
Array concatYes, with customizerNo, merges by indexNo, arrays replaceYou decide
Best useCustom deep merge rulesDefault deep mergeCustom shallow assignmentVery specialized rules

Reach for _.mergeWith when most of your data can use normal deep merge, but a few value types or fields need special handling.

Pitfalls to avoid

Return

Returning too much disables default merge

Only return a custom value for cases you truly handle. For everything else, return undefined.

Mutation

The destination still mutates

_.mergeWith(defaults, overrides, customizer) rewrites defaults. Use {} first when the destination should be fresh.

Arrays

Check both array values when needed

Most concat customizers check Array.isArray(objValue). If a source can provide arrays where the destination is missing, decide how that should behave.

Security

Avoid untrusted merge sources

Like _.merge, this method can expose prototype pollution risks if attacker-controlled objects are merged into shared targets.

❓ FAQ

_.mergeWith() is _.merge() plus a customizer function. It recursively merges source objects into the destination, but lets you override how individual values are combined.
Yes. Like _.merge(), it mutates and returns the destination object. Use _.mergeWith({}, a, b, customizer) when you want a fresh result object.
Return a concrete value to use that value for the merged property. Return undefined to let Lodash fall back to its default deep-merge behavior.
The customizer receives (objValue, srcValue, key, object, source, stack). Most examples only need objValue and srcValue, but key and the parent objects are useful for field-specific rules.
Check whether both values are arrays and return objValue.concat(srcValue). If the values are not arrays, return undefined so Lodash keeps its normal merge behavior.
Both are recursive and both mutate the destination. _.merge() always uses Lodash's default merge rules; _.mergeWith() lets a customizer replace those rules for selected values.

Summary

Did you know?

_.mergeWith calls your customizer for each merge conflict. Return undefined when you want Lodash to continue with its normal recursive merge; return anything else to use your custom value.

About the author

Mari Selvan M P
Mari Selvan M P 🔗

Developer, cloud engineer, and technical writer

  • Experience 12 years building web and cloud systems
  • Focus Full Stack Development, AWS, and Developer Education

I write practical tutorials so students and working developers can learn by doing—from databases and APIs to deployment on AWS.

5 people found this page helpful