Overview

Prefer native JavaScript over Lodash for new projects. Modern JavaScript (ES2020+) covers array manipulation, object transformation, and most utility patterns that Lodash was written to fill in 2012. Import Lodash only when you need its specific advantages: _.cloneDeep, _.merge, _.debounce, or _.throttle. Even then, import the specific function, not the full library, to avoid adding 70 KB to your bundle. See general-principles for the general rule on dependencies.

When native JS wins

Native JavaScript covers most utility needs in 2026.

  • Array methods: Array.from, Array.prototype.flat, flatMap, findIndex, at(-1) cover the patterns that _.flatten, _.compact, _.find, and _.last once solved.
  • Object handling: Object.entries, Object.fromEntries, structuredClone, and spread syntax cover most _.pick, _.omit, _.mapValues use cases.
  • structuredClone (Node 17+, all modern browsers): deep clones any structured-clonable value without a dependency. Replaces _.cloneDeep for objects without functions or class instances.
  • Optional chaining and nullish coalescing: a?.b?.c ?? fallback replaces _.get(a, 'b.c', fallback) with zero overhead.
  • Promise.all, Promise.allSettled: covers _.chunk-and-map patterns for parallel async work.
  • Bundle impact: removing a full Lodash import saves 70 KB (unparsed); even cherry-picked imports add 2 to 5 KB per utility. Native has zero bundle cost.
// Native equivalents
const flat = arr.flat(Infinity);               // _.flattenDeep
const last = arr.at(-1);                       // _.last
const clone = structuredClone(obj);            // _.cloneDeep (for plain objects)
const val = a?.b?.c ?? "default";              // _.get with default
const grouped = Object.groupBy(arr, (x) => x.key); // _.groupBy (ES2024)

When Lodash wins

Lodash is justified in four concrete cases.

  • _.cloneDeep for non-structured-clonable values: objects with methods, class instances, circular references, or Symbols. structuredClone throws on these; _.cloneDeep handles them.
  • _.merge for deep recursive merge: Object.assign and spread are shallow. _.merge recursively merges nested objects without replacing them. Common in config systems.
  • _.debounce and _.throttle: the native equivalent requires hand-rolling a closure with setTimeout. Lodash’s implementation handles leading/trailing edges and cancellation correctly. Small lodash-debounce-style packages exist as alternatives.
  • Legacy codebase with heavy Lodash usage and no time budget for refactoring: stay on it. The incremental migration approach is fine; replace calls as you touch files.

When importing specific Lodash functions, use the per-method package or named import from lodash-es to allow tree-shaking.

// Tree-shakeable import
import { debounce } from "lodash-es";
import cloneDeep from "lodash/cloneDeep";

Trade-offs at a glance

DimensionLodashNative JS
Bundle size70 KB full; 1 to 5 KB per cherry-picked fnZero
Deep clone_.cloneDeep (handles all types)structuredClone (structured-clonable only)
Deep merge_.mergeNo built-in; spread is shallow
Debounce/throttleBattle-tested, cancelableHand-rolled or separate package
Array utilitiesBroad; many legacy helpersModern methods cover 90% of cases
Null-safe access_.get with defaultOptional chaining + nullish coalescing
TypeScript types@types/lodash requiredNative
MaintenanceStable; infrequent releasesN/A
LearnabilityExtra API surface for new contributorsStandard JS; universal knowledge
Browser support targetIE11 safe historicallyES2020+ required for modern methods

Migration cost

Lodash to native migration is file-by-file and low-risk.

  • Use eslint-plugin-you-dont-need-lodash-underscore to identify functions with native equivalents. It reports and auto-fixes many cases.
  • Replace _.get(obj, path, default) with optional chaining and ??. Replace _.flatten with .flat(). Replace _.uniq with [...new Set(arr)].
  • Keep _.cloneDeep, _.merge, _.debounce, and _.throttle until you have specific replacements verified.
  • Remove lodash from package.json once all usages are gone and tests pass.
  • Effort: one engineer-day per 100 Lodash callsites, depending on complexity.

Recommendation

  • New project: no Lodash. Start with native. Add lodash-es only when _.cloneDeep, _.merge, _.debounce, or _.throttle are genuinely needed.
  • Existing project with Lodash: migrate incrementally as you touch files. Prioritize removing import _ from 'lodash' (full import) first; it has the largest bundle impact.
  • Bundle-sensitive app (PWA, mobile web): audit with a bundle analyzer and remove Lodash early. See vite-vs-webpack for analyzer tooling.