Sass map.deep-merge() Function

Beginner
⏱️ 12 min read
📚 Updated: Jul 2026
🎯 5 Examples
sass:map · Dart Sass 1.27+

What You’ll Learn

map.deep-merge() belongs to the built-in sass:map module. It merges two maps and recursively merges nested maps. This page covers deep vs shallow map.merge, theme overrides, immutability, Dart Sass 1.27+ support, and five compiled examples.

01

Concept

Recursive merge

02

Module

@use "sass:map"

03

vs merge

Deep vs shallow

04

Conflicts

$map2 wins

05

Since

Dart Sass 1.27+

06

Practice

5 examples

What Is map.deep-merge()?

Design systems often store config as nested maps—fonts, colors, breakpoints. You want a base map plus overrides without wiping an entire nested branch. Official docs: map.deep-merge is identical to map.merge, except nested map values are also recursively merged.

  • Both maps keep top-level keys that do not collide
  • When both have a map at the same key, those maps are merged together
  • When values are not both maps, $map2’s value replaces $map1’s
💡
Beginner tip

Think of two folders with the same name: deep-merge opens both and combines files inside. Shallow map.merge throws away the first folder and keeps only the second.

📝 Syntax

Load the map module, then call the function:

styles.scss
@use "sass:map";

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

Parameters

ParameterTypeRequiredDescription
$map1MapYesBase map. Keys that also appear here keep their relative order in the result.
$map2MapYesOverlay map. New keys are appended; conflicting non-map values replace $map1.

Return value

TypeResult
MapA new map with top-level and nested keys combined recursively

📦 Loading sass:map

styles.scss
// Recommended — namespaced
@use "sass:map";
$config: map.deep-merge($base, $overrides);

// Optional — bring members into scope
@use "sass:map" as *;
$config: deep-merge($base, $overrides);

// Optional — custom namespace
@use "sass:map" as m;
$config: m.deep-merge($base, $overrides);
⚠️
No legacy global deep-merge()

Unlike older helpers such as map-merge, there is no separate global deep-merge() outside the module API. Use sass:map.

🔍 map.deep-merge vs map.merge

This is the key beginner trap. Official Helvetica weights sample shows it clearly.

styles.scss
@use "sass:map";

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

// deep-merge keeps all four weight keys
map.deep-merge($helvetica-light, $helvetica-heavy);

// merge replaces the whole "weights" map with $helvetica-heavy’s only
map.merge($helvetica-light, $helvetica-heavy);
map.deep-mergemap.merge
Nested mapsMerged recursivelyReplaced wholesale
Flat mapsSame idea as mergeCombine keys; $map2 wins
Best forTheme / config treesSimple one-level maps
SinceDart Sass 1.27+Long-standing (module since 1.23+)

📋 When Values Are Not Both Maps

Recursion only happens when both sides store a map at that key. If $map2 puts a color, number, or other scalar where $map1 had a map, the scalar wins.

styles.scss
@use "sass:map";

$left: ("token": ("size": 16px));
$right: ("token": 1rem);

map.deep-merge($left, $right);
// ("token": 1rem) — not a nested merge

🛠 Compatibility

This is about Sass compilers, not browsers. Compiled CSS only contains the finished values you read from the merged map (for example with map.get).

Implementationmap.deep-merge()Notes
Dart Sass (1.27+)Yes — requiredDeep map helpers landed in 1.27.0
LibSassNoNo modern sass:map deep API
Ruby SassNoLegacy—prefer Dart Sass

⚡ Quick Reference

GoalCode
Import map@use "sass:map";
Deep mergemap.deep-merge($base, $overrides)
Shallow mergemap.merge($base, $overrides)
Read nestedmap.get($merged, "colors", "primary")
Keep result$config: map.deep-merge($config, $layer);
Minimum Dart Sass1.27+

Examples Gallery

Each example uses @use "sass:map". Open View Compiled CSS for verified Dart Sass output (official samples where noted).

📚 Getting Started

Official docs sample: deep merge vs shallow merge.

Example 1 — Helvetica Weights (Official Docs)

Deep merge keeps light and heavy weights; shallow merge drops the light ones.

styles.scss
@use "sass:map";
@use "sass:meta";

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

@debug map.deep-merge($helvetica-light, $helvetica-heavy);
@debug map.merge($helvetica-light, $helvetica-heavy);

.compare {
  --deep: #{meta.inspect(map.deep-merge($helvetica-light, $helvetica-heavy))};
  --shallow: #{meta.inspect(map.merge($helvetica-light, $helvetica-heavy))};
}

How It Works

Both maps use the nested key "weights". Deep merge combines the four weight entries. Shallow merge replaces the entire nested map with the heavy map only.

Example 2 — Theme Override Layer

Merge a base design token map with brand overrides, then read nested colors.

styles.scss
@use "sass:map";
@use "sass:meta";

$base: (
  "colors": (
    "primary": #336699,
    "muted": #94a3b8
  ),
  "radius": 8px
);
$theme: (
  "colors": (
    "accent": #e67e22,
    "primary": #1d4ed8
  ),
  "shadow": 0 4px 12px rgba(0, 0, 0, 0.12)
);
$merged: map.deep-merge($base, $theme);

.theme {
  --primary: #{map.get($merged, "colors", "primary")};
  --accent: #{map.get($merged, "colors", "accent")};
  --muted: #{map.get($merged, "colors", "muted")};
  --radius: #{map.get($merged, "radius")};
  --shadow: #{meta.inspect(map.get($merged, "shadow"))};
}

How It Works

primary is overridden, muted is kept from the base, accent and shadow are added, and radius survives at the top level.

📈 Practical Patterns

Nested keys, immutability, and scalar replacement.

Example 3 — Nested Key Override Inside a Shared Branch

Combine sibling nested keys and override a shared nested value.

styles.scss
@use "sass:map";
@use "sass:meta";

$a: ("a": 1, "nested": ("x": 10, "y": 20));
$b: ("b": 2, "nested": ("y": 99, "z": 30));
$c: map.deep-merge($a, $b);

.compare {
  --all: #{meta.inspect($c)};
  --y: #{map.get($c, "nested", "y")};
}

How It Works

Nested x stays from $a, z arrives from $b, and shared y becomes 99 because $map2 wins.

Example 4 — Original Map Stays Unchanged

Deep merge returns a copy—reassign if you want to update a variable.

styles.scss
@use "sass:map";
@use "sass:meta";

$orig: ("a": ("b": 1));
$copy: map.deep-merge($orig, ("a": ("c": 2)));

.immut {
  --orig: #{meta.inspect($orig)};
  --copy: #{meta.inspect($copy)};
}

How It Works

$orig still has only b. The merged copy adds c under the nested a map without mutating the source.

Example 5 — Scalar Beats Nested Map

When $map2 is not a map at that key, recursion stops and replacement wins.

styles.scss
@use "sass:map";
@use "sass:meta";

$left: ("token": ("size": 16px));
$right: ("token": 1rem);
$override: map.deep-merge($left, $right);

.scalar {
  --token: #{meta.inspect(map.get($override, "token"))};
}

How It Works

The nested ("size": 16px) map is replaced by 1rem. Deep merge does not try to merge a map with a length value.

🚀 Real-World Use Cases

  • Design-system themes — layer brand overrides onto shared defaults.
  • Component configs — merge partial option maps without losing siblings.
  • Multi-brand builds — deep-merge per-brand token files into one config.
  • Plugin / mixin APIs — accept user maps and merge onto safe defaults.
  • Nested typography scales — combine weight/size branches like the docs sample.

🧠 How Compilation Works

1

Write SCSS

Call map.deep-merge($map1, $map2) after @use "sass:map".

Source
2

Walk both maps

Dart Sass merges top-level keys and recurses when both values are maps.

Compile
3

Return a new map

Read values with map.get (including nested key paths).

Result
4

CSS sees tokens

Only the finished colors, sizes, or shadows you emit remain.

⚠️ Common Pitfalls

  • Using merge by habit — shallow merge drops nested siblings you meant to keep.
  • Forgetting to reassign — deep-merge returns a copy; it does not mutate in place.
  • Scalar overrides — a non-map in $map2 replaces a whole nested map.
  • Old Dart Sass — need 1.27+ for map.deep-merge.
  • Expecting a global name — there is no legacy deep-merge() outside sass:map.

💡 Best Practices

✅ Do

  • Use @use "sass:map" and map.deep-merge() for nested configs
  • Put defaults in $map1 and overrides in $map2
  • Read results with nested map.get($map, $k1, $k2, …)
  • Reassign when updating a stored config variable
  • Prefer Dart Sass 1.27+

❌ Don’t

  • Expect the browser to evaluate map.deep-merge
  • Use shallow map.merge when nested keys must survive
  • Assume originals mutate without reassignment
  • Rely on LibSass for deep map helpers
  • Pass a scalar overlay if you still need the nested map

Key Takeaways

Knowledge Unlocked

Five things to remember about map.deep-merge()

Recursive nested merge—$map2 wins conflicts; returns a new map.

5
Core concepts
📦 02

Module

sass:map

@use
🔢 03

Nested maps

merge recursively

Rule
04

vs merge

shallow replaces branch

Safe
05

Prefer

Dart Sass 1.27+

Tooling

❓ 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 the same way—prefer current Dart Sass.
Did you know?

Official Sass docs highlight deep operations because libraries and design systems often share nested configuration maps. Helpers like map.get with multiple keys, map.deep-remove, and map.deep-merge are built for that pattern.

Conclusion

map.deep-merge() recursively merges nested Sass maps so theme and config layers keep sibling keys. Load it through sass:map, prefer it over shallow map.merge for nested trees, and reassign to keep the returned copy.

Continue with map.deep-remove() or the Sass introduction.

Next: remove nested keys

Learn map.deep-remove() to prune tokens from nested config maps.

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.

5 people found this page helpful