PureDevTools

JSON Merge Tool

Merge two JSON objects — deep merge, shallow merge, array concat, and conflict resolution. All in your browser.

All processing happens in your browser. No data is sent to any server.
Merge Strategy
Array Strategy
Conflict Resolution
Indent
Left JSON
Right JSON
Merged Result
Conflicts resolved4
  • versionleft: "1.0.0"right: "2.0.0"resolved: "2.0.0"
  • settings.languageleft: "en"right: "es"resolved: "es"
  • settings.timeoutleft: 30right: 60resolved: 60
  • debugleft: falseright: trueresolved: true

You have a default config JSON and a user override JSON. You need to merge them so user values overwrite defaults, but nested objects should be deep-merged (not replaced entirely), and arrays should be concatenated (not overwritten). Object.assign() does shallow merge. {...defaults, ...overrides} also shallow. Writing a recursive deep merge function handles the common case but breaks on arrays, null values, and conflicting types.

Why This Tool (Not Writing Your Own Merge)

A correct deep merge handles 5+ edge cases: nested objects, arrays (concat vs replace), null vs undefined, type conflicts, and circular references. This tool lets you paste two JSON objects, choose your strategy (deep merge, shallow merge, array concat), pick conflict resolution (left wins, right wins, combine), and get the merged result — without writing and debugging merge logic. Everything runs in your browser; no data is sent anywhere.

What Is JSON Merging?

JSON merging combines two JSON objects into one. The result contains all keys from both inputs, with a configurable strategy for resolving conflicts when the same key appears in both objects.

Typical use cases:

Shallow Merge vs Deep Merge

Shallow merge

Shallow merge combines only the top-level keys of both objects. When the same key appears in both, conflict resolution determines the winner. Nested objects are treated as atomic values — they are replaced wholesale, never recursively combined.

// Left
{ "settings": { "theme": "dark", "lang": "en" }, "debug": false }

// Right
{ "settings": { "lang": "es", "size": 16 }, "debug": true }

// Shallow merge, right wins
{ "settings": { "lang": "es", "size": 16 }, "debug": true }

Notice that settings.theme is lost — shallow merge replaced the entire settings object.

Deep merge

Deep merge recursively traverses nested objects, merging their keys at every level. Only scalar values (strings, numbers, booleans, null) or type-mismatched pairs at the same path trigger conflict resolution.

// Left
{ "settings": { "theme": "dark", "lang": "en" }, "debug": false }

// Right
{ "settings": { "lang": "es", "size": 16 }, "debug": true }

// Deep merge, right wins
{ "settings": { "theme": "dark", "lang": "es", "size": 16 }, "debug": true }

Deep merge preserved settings.theme from the left object while adding settings.size from the right and resolving the settings.lang conflict in favor of the right.

Array Strategies (Deep Merge)

When both objects have an array at the same key path, the array strategy controls the outcome.

Replace (default)

The right array completely replaces the left array. Use this when the right side represents the authoritative list.

// Left: { "tags": ["frontend", "typescript"] }
// Right: { "tags": ["react", "astro"] }
// Result: { "tags": ["react", "astro"] }

Concat

Both arrays are joined — left items first, right items after. Duplicates are preserved.

// Left: { "tags": ["frontend", "typescript"] }
// Right: { "tags": ["react", "astro"] }
// Result: { "tags": ["frontend", "typescript", "react", "astro"] }

Union

Both arrays are concatenated and then deduplicated by value. Duplicates (compared by JSON serialization) are removed, keeping the first occurrence.

// Left: { "tags": ["frontend", "react"] }
// Right: { "tags": ["react", "astro"] }
// Result: { "tags": ["frontend", "react", "astro"] }
// ("react" appears once because it's deduplicated)

Conflict Resolution

A conflict occurs when both objects have a value at the same key path and those values cannot be recursively merged (in deep mode) or when the same top-level key exists in both (in shallow mode).

Left wins

The left object’s value is kept. The right value is discarded. Use this when the left object represents the source of truth.

// Left: { "version": "1.0.0" }   Right: { "version": "2.0.0" }
// Left wins → { "version": "1.0.0" }

Right wins (default)

The right object’s value overrides the left. This is the most common merging behavior — right is the override/patch layer.

// Left: { "version": "1.0.0" }   Right: { "version": "2.0.0" }
// Right wins → { "version": "2.0.0" }

Combine

Both values are preserved by wrapping them in an array: [leftValue, rightValue]. No data is discarded. Use this when you want to review all conflicts and decide later.

// Left: { "version": "1.0.0" }   Right: { "version": "2.0.0" }
// Combine → { "version": ["1.0.0", "2.0.0"] }

Conflict Reporting

Every conflict found during merging is listed in the Conflicts resolved panel below the output. Each entry shows:

If no conflicts are detected, the tool reports “No conflicts” — meaning all keys are unique between the two objects.

Common Patterns

Base config + environment override

A common Node.js / application config pattern:

// base.json
{
  "port": 3000,
  "db": { "host": "localhost", "port": 5432, "ssl": false },
  "log": { "level": "info", "format": "text" }
}

// production.json
{
  "db": { "host": "db.prod.example.com", "ssl": true },
  "log": { "level": "warn", "format": "json" }
}

// Deep merge, right wins → effective production config
{
  "port": 3000,
  "db": { "host": "db.prod.example.com", "port": 5432, "ssl": true },
  "log": { "level": "warn", "format": "json" }
}

Merging partial API responses

// Response A (user profile)
{ "id": "u1", "name": "Alice", "email": "alice@example.com" }

// Response B (user preferences)
{ "id": "u1", "theme": "dark", "language": "en" }

// Shallow merge, left wins (keep first id)
{ "id": "u1", "name": "Alice", "email": "alice@example.com", "theme": "dark", "language": "en" }

Package.json dependency merging

// Base dependencies
{ "dependencies": { "react": "^19.0.0", "typescript": "^5.7.0" } }

// Additional dependencies to add
{ "dependencies": { "astro": "^5.0.0", "tailwindcss": "^4.0.0" } }

// Deep merge → combined dependencies
{
  "dependencies": {
    "react": "^19.0.0",
    "typescript": "^5.7.0",
    "astro": "^5.0.0",
    "tailwindcss": "^4.0.0"
  }
}

Frequently Asked Questions

Why must both inputs be JSON objects? Merging is defined for objects (key-value maps). Arrays and primitives at the root level don’t have a meaningful merge semantic — you can’t merge [1, 2] with [3, 4] in a keyed way. If you need to combine root-level arrays, wrap them in an object: { "items": [1, 2] }.

Does shallow merge modify nested objects? No. In shallow merge, nested objects are treated as atomic values. The entire nested object from the right side replaces the one from the left when there is a conflict. Use deep merge if you need recursive combination.

What happens with null values? null is a valid JSON value and is treated as a scalar. If the left value is null and the right is an object (or vice versa), this is a conflict handled by the selected conflict resolution strategy.

Can I merge more than two objects? The tool merges two objects at a time. To merge three or more, merge the first two, copy the result, paste it as the new left input, and merge with the third object.

Is the order of keys preserved in the output? The output preserves insertion order as specified by the ECMAScript specification for JSON.stringify. Left object keys appear first (in their original order), followed by any new keys from the right object.

Does “Union” array deduplication work for nested objects? Yes — items are compared by their JSON serialization (via JSON.stringify). Two nested objects are considered duplicates only if they have identical keys and values in the same order. Objects with different key orders but same content are treated as distinct.

Is my data sent to a server? No. All merging runs entirely in your browser. Your JSON is never transmitted anywhere, stored, or logged. The tool works offline once the page has loaded.

Related Tools

More JSON Tools