Merging JSON Objects: Deep Merge, Spread, and Other Strategies

jsonjavascriptmergepatterns

Merging JSON objects is essential for combining configs, updating data, and handling defaults. Here are the strategies you should know.

Shallow Merge with Spread

const defaults = { theme: "light", lang: "en", fontSize: 14 };

const user = { theme: "dark", fontSize: 16 };

const merged = { ...defaults, ...user };

// { theme: "dark", lang: "en", fontSize: 16 }

Properties in the second object overwrite the first.

Object.assign()

const merged = Object.assign({}, defaults, user);

Same behavior as spread, but mutates the target if you skip {}.

Deep Merge

Shallow merge doesn't handle nested objects:

// Shallow merge loses nested data

const a = { config: { theme: "light", lang: "en" } };

const b = { config: { theme: "dark" } };

const shallow = { ...a, ...b };

// { config: { theme: "dark" } } — lang is lost!

Deep merge recursively combines nested objects:

function deepMerge(target, source) {

const result = { ...target };

for (const key of Object.keys(source)) {

if (isObject(target[key]) && isObject(source[key])) {

result[key] = deepMerge(target[key], source[key]);

} else {

result[key] = source[key];

}

}

return result;

}

const deep = deepMerge(a, b);

// { config: { theme: "dark", lang: "en" } } — lang preserved!

Array Merging Strategies

When both objects have arrays:

// Replace (default behavior)

const replace = { ...a, items: b.items };

// Concatenate

const concat = { ...a, items: [...a.items, ...b.items] };

// Merge by ID

const mergeById = {

...a,

items: a.items.map(item => {

const override = b.items.find(i => i.id === item.id);

return override ? { ...item, ...override } : item;

})

};

Using Libraries

For production code, use established libraries:

  • lodash.merge — Battle-tested deep merge
  • deepmerge — Customizable merge behavior
  • ts-deepmerge — TypeScript-friendly
  • Practical Example: Config Merging

    const defaultConfig = {

    api: { timeout: 5000, retries: 3 },

    ui: { theme: "light", language: "en" }

    };

    const envConfig = {

    api: { timeout: 10000 },

    ui: { theme: "dark" }

    };

    const finalConfig = deepMerge(defaultConfig, envConfig);

    // api.timeout: 10000, api.retries: 3, ui.theme: "dark", ui.language: "en"

    Format your merged results with our JSON Formatter for easy inspection.

    Related Tools