Skip to main content

defaults()

defaults<T>(object, ...sources): T

Assigns source properties to destination only where destination is undefined.

πŸ’Ž Why is this a Hidden Gem?

Safely applies default values only where properties are undefined.

note

Only undefined values are replaced. null values are preserved.


Type Parameters​

T: T extends Record<string, unknown>​

The type of the destination object.


Parameters​

object: T​

The destination object.

sources: ...Record<string, unknown>[]​

The source objects.


Returns: T​

A new object with defaults applied (does not mutate original).


Since​

2.0.0


Note​

Inherited properties (e.g., from Object.prototype) are not considered undefined.


Performance​

O(nΓ—m) where n = number of sources, m = average keys per source


Also known as​

assign (Radashi) · defaults (Lodash, es-toolkit) · mergeRight (Ramda) · ❌ (Remeda, Effect, Modern Dash, Antfu)


Example​

const target = { a: 1, b: undefined, c: 3 };
const source = { b: 2, c: 4, d: 5 };

defaults(target, source);
// => { a: 1, b: 2, c: 3, d: 5 }

// Original is not mutated
target.b; // => undefined (unchanged)

// null is preserved (not undefined)
defaults({ city: null }, { city: 'Paris' });
// => { city: null }

// Multiple sources (first wins)
defaults({ a: 1 }, { b: 2 }, { b: 3, c: 4 });
// => { a: 1, b: 2, c: 4 }

How it works?​

Fills in undefined properties with values from source objects. Only undefined values are replaced β€” null is preserved.

null vs undefined​

null is preserved because it's a deliberate value, not absence.

Multiple Sources​

First source wins for each key.


Use Cases​

Initialize configuration options πŸ“Œβ€‹

Apply default settings to a user-provided config object without overwriting explicit nulls. Perfect for library initialization or function parameters.

const config = defaults(userOptions, {
timeout: 5000,
retries: 3,
verbose: true
});

Preserve explicit nulls​

Ensure that null values provided by the user are respected (not treated as missing). Critical for APIs where null has a specific semantic meaning (e.g., "disable feature").

const options = defaults({ cache: null }, { cache: true });
// options.cache is null (user disabled it)

Combine multiple sources​

Layer multiple default objects to build a final configuration. Useful for cascading settings (Global -> Project -> User).

const final = defaults(userOpts, projectDefaults, globalDefaults);

Apply overlay positioning defaults​

Merge user overlay options with sensible positioning defaults. Essential for overlay/dialog systems with configurable but defaulted positioning.

const overlayConfig = defaults(userOptions, {
hasBackdrop: true,
backdropClass: "overlay-backdrop",
panelClass: "",
width: "auto",
height: "auto",
minWidth: 200,
maxWidth: "80vw",
scrollStrategy: "block",
positionStrategy: "global",
direction: "ltr",
});

overlayService.create(overlayConfig);

Initialize drag-and-drop configuration​

Apply default drag-and-drop behavior with user overrides. Perfect for CDK-style drag-and-drop directives with sensible defaults.

const dragConfig = defaults(userDragOptions, {
lockAxis: null,
dragStartThreshold: 5,
pointerDirectionChangeThreshold: 5,
autoScrollDisabled: false,
autoScrollStep: 2,
boundaryElement: null,
previewClass: "drag-preview",
placeholderClass: "drag-placeholder",
});

initDragDrop(element, dragConfig);

Apply chart rendering defaults​

Merge user chart options with sensible defaults. Essential for chart components that need consistent baseline configuration.

const chartConfig = defaults(userOptions, {
width: 600,
height: 400,
showGrid: true,
showLegend: true,
colors: ["#3b82f6", "#ef4444", "#22c55e"],
animation: { duration: 300, easing: "ease-out" },
});

renderChart(data, chartConfig);

Initialize PWA install prompt state​

Set default state for the PWA install banner with user overrides. Perfect for managing the "Add to Home Screen" prompt lifecycle.

const installState = defaults(loadSavedState(), {
dismissed: false,
dismissedAt: null,
installCount: 0,
platform: "unknown",
});

if (!installState.dismissed) {
showInstallBanner();
}