Lodash _.isEqualWith() method

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

What you’ll learn

  • How _.isEqualWith(value, other, customizer) layers overrides onto deep equality.
  • When returning undefined delegates back to lodash defaults.
  • How explicit true / false results short-circuit comparisons.
  • Patterns like tolerant string matching versus forcing rejection.

Prerequisites

You already understand _.isEqual basics and can write small comparator callbacks.

  • You know deep equality cares about nested shapes, not only references.
  • Try-it labs load lodash from the CDN.

Overview

Reach for _.isEqualWith when business rules diverge from lodash defaults: locale-aware strings, numeric tolerance, branded wrappers, or selectively ignoring volatile metadata fields.

Customizer hook

Inspect each compared pair before lodash decides.

undefined fallback

Return nothing to keep standard deep comparison for that pair.

Explicit vote

Return booleans to approve or veto equality immediately.

Syntax

javascript
_.isEqualWith(value, other, [customizer])
  • value / other: values to compare (same role as in _.isEqual).
  • customizer (optional): (objValue, othValue [, index|key, object, other, stack]) — return undefined to defer.
  • Returns: true if equivalent under custom rules; otherwise false.
1

Treat alternate greetings as equal

Official lodash pattern: treat "hello" and "hi" as interchangeable while other slots stay strict.

javascript
import isEqual from "lodash/isEqual";
import isEqualWith from "lodash/isEqualWith";

function isGreeting(value) {
  return /^h(?:i|ello)$/.test(value);
}

function greetingCustomizer(objValue, othValue) {
  if (isGreeting(objValue) && isGreeting(othValue)) {
    return true;
  }
}

var left = ["hello", "goodbye"];
var right = ["hi", "goodbye"];

console.log(
  "withCustomizer: " + isEqualWith(left, right, greetingCustomizer) + "\n" + // true
  "plainIsEqual: " + isEqual(left, right)                                   // false
);
Try it Yourself
2

Force false at the root pair

If the customizer returns false for the top-level arguments, lodash stops there even when structures match.

javascript
import isEqual from "lodash/isEqual";
import isEqualWith from "lodash/isEqualWith";

var a = { id: 1 };
var b = { id: 1 };

function alwaysRejectRoot() {
  return false;
}

console.log(
  "withoutCustomizer: " + isEqual(a, b) + "\n" +              // true
  "equalWithReject: " + isEqualWith(a, b, alwaysRejectRoot)   // false
);
Try it Yourself
3

Case-insensitive nested strings

Return booleans only for strings; return undefined elsewhere so nested objects still compare normally.

javascript
import isEqual from "lodash/isEqual";
import isEqualWith from "lodash/isEqualWith";

function ignoreCaseCustomizer(objValue, othValue) {
  if (typeof objValue === "string" && typeof othValue === "string") {
    return objValue.toLowerCase() === othValue.toLowerCase();
  }
}

console.log(
  "caseInsensitive: " +
    isEqualWith({ tag: "Hello" }, { tag: "hello" }, ignoreCaseCustomizer) +
    "\n" +                                                                    // true
  "plainIsEqual: " +
    isEqual({ tag: "Hello" }, { tag: "hello" })                             // false
);
Try it Yourself

📋 _.isEqualWith vs related checks

APIMatches
_.isEqualWith(a, b, fn)Deep equality with per-pair overrides from fn.
_.isEqual(a, b)Same deep walk without custom overrides.
_.isMatchWith(a, b, fn)Partial structural match with optional customizer.
JSON.stringifyFragile string hack; keys/order/types often diverge from real semantics.

Pitfalls to avoid

Contracts

Over-broad customizers

Returning true too eagerly can hide real mismatches—narrow conditions (types, keys, tags).

Perf

Heavy work per pair

Deep trees invoke customizers often; keep regex, parsing, and allocation out of tight loops.

Semantics

undefined vs missing return

Explicit return undefined reads clearly versus falling through accidentally with mixed branches.

❓ FAQ

_.isEqual uses lodash defaults only. _.isEqualWith adds a customizer that can approve or reject comparisons before the default deep logic runs.
Return undefined for pairs you want lodash to compare normally (including nested recursion). Return true or false only when you want to override the outcome.
Lodash passes your customizer through the deep comparison so it can be invoked for nested values too; extra arguments can include key/index context depending on parent structures.
Avoid calling _.isEqualWith again blindly inside the customizer on the same subgraph without guards; lodash tracks cycles internally, but your callback should stay cheap.

Summary

  • Purpose: extend deep equality with domain-specific comparison hooks.
  • Remember: only return booleans when overriding; otherwise return undefined.
  • Next: explore more on Lodash _.isError().
Did you know?

When your customizer returns undefined, lodash falls back to normal deep comparison for that pair (with your customizer still available deeper in the walk). Any other return value is coerced with !!, so false forces inequality even if structures match.

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.

6 people found this page helpful