Lodash _.defaults() method
What you’ll learn
- How
_.defaults(object, ...sources)fills onlyundefinedkeys on the destination—the opposite precedence of _.assign. - Why
nullcounts as “already set” and is not replaced. - How to keep callers’ objects untouched by passing
{}as the destination. - Try each example in the editor (
?tryit=1,2,3) with Lodash from a CDN.
Prerequisites
Read _.assign() first—_.defaults uses the same shallow / mutating shape but flips the precedence rule.
- undefined vs null: only
undefinedis treated as “missing”. Knowing the distinction prevents surprise no-ops. - Mutation awareness: the destination is written in place and returned—callers sharing the reference will see the new keys.
Overview
_.defaults walks each source left to right and writes a key onto the destination only when the destination’s current value is undefined. The original object always wins; among sources, the first one to define a key wins.
Original wins
User-supplied values are protected: _.defaults never overwrites an existing key.
First source wins
Among sources, the earliest one to define a key fills the gap—opposite of _.assign’s last-wins rule.
Shallow
Nested objects on the destination block all defaults below them—use _.defaultsDeep for recursion.
Syntax
_.defaults(object, [...sources]) - object: destination; mutated in place.
- sources: zero or more source objects walked left to right; only own enumerable string keys are considered.
- Write rule: a key is written to
objectonly when its current value isundefined. - Returns: the (now-mutated) destination object.
Fill in only the missing keys
The user’s theme survives because it’s already defined; language comes from the defaults object because the user didn’t supply one.
import defaults from "lodash/defaults";
const userSettings = { username: "JohnDoe", theme: "light" };
const defaultSettings = { theme: "dark", language: "en" };
defaults(userSettings, defaultSettings);
// userSettings -> { username: "JohnDoe", theme: "light", language: "en" }
// ^^^^^^^^^^^^ ^^^^^^^^^^^^^^
// user wins default fills First source to define a key wins
Once a key is set on the destination, no later source can change it—the inverse of _.assign. Useful when you want a primary defaults bundle plus an optional fallback bundle.
import defaults from "lodash/defaults";
const user = { a: 1 };
const primary = { b: 2, c: 3 };
const fallback = { b: "ignored", c: "ignored", d: 4 };
defaults(user, primary, fallback);
// user -> { a: 1, b: 2, c: 3, d: 4 }
// ^^^^^^^^^^^^ ^^^^^^^
// primary wins fallback only fills "d" null counts as “already set”
Only literal undefined is replaced. A null on the destination blocks the default. Also note the “pass {} as the destination” pattern for keeping callers’ objects untouched.
import defaults from "lodash/defaults";
const userPrefs = { theme: null, lang: undefined };
const defs = { theme: "light", lang: "en", fontSize: 14 };
const result = defaults({}, userPrefs, defs);
// result -> { theme: null, lang: "en", fontSize: 14 }
// ^^^^^^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^^^^
// null kept undefined no key at all
// replaced -> filled by defs
console.log(userPrefs);
// { theme: null, lang: undefined } (untouched) 📋 _.defaults vs _.assign vs _.defaultsDeep
| Topic | _.defaults | _.assign | _.defaultsDeep |
|---|---|---|---|
| Precedence | Destination wins; first source fills | Last source wins (overwrites) | Destination wins; first source fills |
Replaces null? | No—only undefined | Yes | No—only undefined |
| Depth | Shallow | Shallow | Recursive (plain objects & arrays) |
| Typical use | Flat options + a fallback bundle | Layer flat configs | Nested config trees |
Reach for _.defaults when you want a no-overwrite merge of flat options; jump to _.defaultsDeep the moment your config nests plain objects.
Pitfalls to avoid
null blocks the default
Only undefined counts as missing. If a form clears a field to null, the default will not step in. Sanitize first or use _.defaults({}, ...) with a pre-clean.
Confusing with _.assign
Old habits expect last-wins. _.defaults(target, primary, fallback) walks primary before fallback—exactly the opposite intuition.
Destination is written in place
Pass {} as the first argument to leave the caller’s options object untouched—essential in shared-config or Redux-style code paths.
❓ FAQ
Summary
- Purpose: fill
undefinedown keys on the destination from sources, left to right. - Remember: destination wins; first source fills;
nullis NOT replaced; shallow only. - Next: Lodash _.defaultsDeep(), _.assign(), or the official Lodash docs for _.defaults.
_.defaults reverses the precedence rule of _.assign. Sources are still walked left to right, but a key is only written when its current value is undefined—so the first source that defines a key wins, and the original object always beats every source.
6 people found this page helpful
