Sass map.deep-merge()

Beginner
11 min read
Updated: Nov 2026
5 examples
Dart Sass 1.27+

Definition and Usage

map.deep-merge($map1, $map2) returns a new map with keys from both sides. It is identical to map.merge(), except nested map values are also merged recursively. Load it with @use "sass:map" on Dart Sass 1.27+. There is no separate global deep-merge().

Official idea.

Design systems often share nested config maps. Deep merge keeps nested keys from both maps instead of wiping a whole nested branch when the same key appears on both sides.

$map2 wins conflicts.

Matching non-map values take $map2’s value. Matching nested maps combine; nested keys from $map2 still override the same nested keys from $map1.

Immutable result.

Original maps stay unchanged. Reassign when you want to keep the merge: $config: map.deep-merge($config, $overrides);

Syntax

SCSS
@use "sass:map";

map.deep-merge($map1, $map2);

// Official pattern — nested "weights" keys are combined
$map1: ("weights": ("lightest": 100, "light": 300));
$map2: ("weights": ("medium": 500, "bold": 700));
map.deep-merge($map1, $map2);
// ("weights": ("lightest": 100, "light": 300, "medium": 500, "bold": 700))

Pattern: base theme map → override map → map.deep-merge → read with map.get (multi-key path supported in Dart Sass).

Deep vs Shallow

Pick the helper based on whether nested maps should combine or replace:

SituationUse
Combine nested maps recursivelymap.deep-merge($base, $over)
Replace a nested map wholesalemap.merge($base, $over)
Merge into one nested path onlymap.merge($map, $keys..., $partial)
Same key, non-map values$map2 value wins
Same key, both values are mapsDeep-merge recurses; shallow merge replaces
Global deep-merge()Not provided — module only
Key order.

Like map.merge, keys that already exist in $map1 keep $map1’s order; new keys from $map2 are appended.

SCSS Examples

Examples target Dart Sass 1.27+ with @use "sass:map". Open View Compiled CSS for verified output (matches official Sass docs samples).

Example 1 — Nested Weights (Official Docs)

Two font maps share a weights key; deep-merge keeps every weight.

SCSS
@use "sass:map";
@use "sass:list";

$helvetica-light: (
  "weights": (
    "lightest": 100,
    "light": 300
  )
);
$helvetica-heavy: (
  "weights": (
    "medium": 500,
    "bold": 700
  )
);

$merged: map.deep-merge($helvetica-light, $helvetica-heavy);

.font {
  --light: #{map.get($merged, "weights", "light")};
  --medium: #{map.get($merged, "weights", "medium")};
  --bold: #{map.get($merged, "weights", "bold")};
  --count: #{list.length(map.keys(map.get($merged, "weights")))};
}

How It Works

  1. Both maps have a nested weights map.
  2. Deep-merge combines all four weight keys.
  3. map.get with a path reads nested values.
  4. Matches the official Sass docs sample.

Example 2 — deep-merge vs merge

Shallow merge replaces the nested weights map entirely.

SCSS
@use "sass:map";
@use "sass:meta";
@use "sass:list";

$helvetica-light: (
  "weights": (
    "lightest": 100,
    "light": 300
  )
);
$helvetica-heavy: (
  "weights": (
    "medium": 500,
    "bold": 700
  )
);

.compare {
  --deep-count: #{list.length(map.keys(map.get(map.deep-merge($helvetica-light, $helvetica-heavy), "weights")))};
  --shallow-count: #{list.length(map.keys(map.get(map.merge($helvetica-light, $helvetica-heavy), "weights")))};
  --shallow: #{meta.inspect(map.merge($helvetica-light, $helvetica-heavy))};
}

How It Works

  1. Deep-merge keeps light + heavy weights (4 keys).
  2. Shallow merge replaces weights with only heavy keys (2).
  3. Official docs show the same contrast.
  4. Use shallow merge only when replacement is intentional.

Example 3 — Theme Overrides

Layer a brand theme over a base design-token map.

SCSS
@use "sass:map";

$base: (
  colors: (primary: blue, secondary: gray),
  space: (sm: 8px, md: 16px)
);
$theme: (
  colors: (primary: navy, accent: teal),
  space: (lg: 24px),
  radius: 4px
);

$config: map.deep-merge($base, $theme);

.theme {
  --primary: #{map.get($config, colors, primary)};
  --secondary: #{map.get($config, colors, secondary)};
  --accent: #{map.get($config, colors, accent)};
  --sm: #{map.get($config, space, sm)};
  --lg: #{map.get($config, space, lg)};
  --radius: #{map.get($config, radius)};
}

How It Works

  1. primary becomes navy from the theme.
  2. secondary survives from the base map.
  3. New keys like accent, lg, and radius are added.
  4. Typical pattern for library defaults + project overrides.

Example 4 — Non-Map Value Replaces a Nested Map

Deep-merge only recurses when both values are maps.

SCSS
@use "sass:map";
@use "sass:meta";

$a: (x: (y: 1), z: 1);
$b: (x: replace-me, z: 2);

$result: map.deep-merge($a, $b);

.override {
  --x: #{meta.inspect(map.get($result, x))};
  --z: #{map.get($result, z)};
}

How It Works

  1. $a’s x is a nested map.
  2. $b’s x is a plain value, so recursion stops.
  3. Result: x becomes replace-me.
  4. Keep types aligned when you expect nested merges.

Example 5 — Reassign to Persist Overrides

Store the merged map when later rules need the same config.

SCSS
@use "sass:map";

$config: (
  button: (pad: 8px, radius: 2px)
);

$config: map.deep-merge($config, (
  button: (radius: 6px, shadow: true)
));

.btn {
  padding: map.get($config, button, pad);
  border-radius: map.get($config, button, radius);
  --shadow: #{map.get($config, button, shadow)};
}

How It Works

  1. Reassignment keeps the merged map in $config.
  2. pad remains from the base map.
  3. radius is overridden to 6px.
  4. shadow is a new nested key from the override.

Edge Cases & Pitfalls

Most deep-merge bugs are expecting recursion when one side is not a map, or forgetting to reassign.

shallow

merge ≠ deep-merge

map.merge replaces colliding nested maps. Use deep-merge when nested keys must combine.

type

Both sides must be maps to recurse

If $map2 puts a color, number, or list under a key that held a map, that nested map is replaced.

assign

Returns a copy

Callers must reassign: $map: map.deep-merge($map, $over);

module

No global deep-merge()

Always load sass:map and call map.deep-merge.

Sass Support

Prefer the module form with Dart Sass 1.27+. There is no global deep-merge().

APIStatus
map.deep-merge()Dart Sass 1.27+ via @use "sass:map"
Global deep-merge()Not provided
Return typeNew map (inputs unchanged)
LibSass / Ruby SassNo map.deep-merge

Compile with the Dart Sass CLI.

Key Takeaways

  • Recursively merges nested maps instead of replacing them.
  • $map2 wins on conflicting non-map values and nested keys.
  • Ideal for theme overrides on nested design tokens.
  • Module only — Dart Sass 1.27+, no global helper.

One line: when nested config maps must combine, call map.deep-merge($base, $overrides).

Compile Notes

Practical checklist:

checklist.txt
1. @use "sass:map"
2. Prefer deep-merge for nested theme/config maps
3. Use map.merge when a nested branch should be replaced
4. Reassign: $config: map.deep-merge($config, $over)
5. Read nested values with map.get($map, $k1, $k2, ...)

Frequently Asked Questions

map.deep-merge($map1, $map2) returns a new map combining both maps. Nested map values are merged recursively—unlike map.merge(), which replaces a whole nested map when keys collide.
Official docs: identical to map.merge(), except nested map values are also recursively merged. Shallow merge replaces the nested weights map entirely; deep-merge keeps keys from both nested maps.
For non-map values, $map2 wins. For nested maps on both sides, keys are combined and $map2’s nested values still override matching nested keys.
No. It returns a new map. Reassign if you need to keep the result: $config: map.deep-merge($config, $overrides).
No. Use map.deep-merge() after @use "sass:map" (or @use "sass:map" as * and call deep-merge()).
Dart Sass 1.27+. LibSass and Ruby Sass do not provide this deep helper—prefer current Dart Sass.

Next: Sass map.deep-remove()

Remove a key from a nested map by following a key path.

map.deep-remove() →

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.

8 people found this page helpful