Combine · JSON Merge

JSON Merge — Deep-Merge Two JSON Documents

Combine two JSON documents with real deep-merge semantics: objects merge recursively, arrays and scalars are replaced, and where both sides define the same key, the Override wins. Runs in your browser.

No sign-up to start · Runs in your browser

JSON Merge
Open merged JSON in editor
Base
Override
Merged JSON

Runs entirely in your browser — nothing is uploaded.

{"app":{"name":"api","port":8080,"log":{"level":"info","format":"text"}},"features":["auth"]}
{"app":{"port":9090,"log":{"level":"debug"}},"features":["auth","cache"],"debug":true}
JSON Merge deep-merges two documents in your browser: paste the Base (your defaults) on top and the Override (your changes) below, then click Merge. Objects combine recursively, so keys unique to either side survive. Where both sides define the same key, the Override value wins, and arrays and scalars are replaced whole rather than concatenated — the standard semantics for layered configuration. Nothing is uploaded, and one click opens the merged result in the full editor.

JSON merge example: base config plus environment override

Base + Override
// Base (defaults)
{
  "app": {
    "name": "api",
    "port": 8080,
    "log": { "level": "info", "format": "text" }
  },
  "features": ["auth"]
}

// Override (staging)
{
  "app": {
    "port": 9090,
    "log": { "level": "debug" }
  },
  "features": ["auth", "cache"],
  "debug": true
}
Merged result
{
  "app": {
    "name": "api",
    "port": 9090,
    "log": {
      "level": "debug",
      "format": "text"
    }
  },
  "features": [
    "auth",
    "cache"
  ],
  "debug": true
}

Every rule is visible here. app.name survives from the Base, app.port takes the Override value, and app.log merges deeply — level is overridden while format is kept. The features array is replaced whole (the Override repeats "auth", so it remains), and debug is appended as a new key. Key order follows the Base, with new Override keys added at the end.

How to merge two JSON files

  1. 1

    Paste the Base JSON

    The defaults go in the top panel — your base configuration, the stored document, or the original version. The loaded example is editable if you want to experiment first.

  2. 2

    Paste the Override JSON

    The changes go in the panel below — an environment overlay, a tenant-specific patch, or the newer copy. Only the keys it defines will affect the result.

  3. 3

    Click Merge

    The deep merge runs instantly on your machine. The status line spells out the semantics after every run: objects combined deeply; on conflicts the Override side wins.

  4. 4

    Check the merged result

    Keys unique to either side survive, nested objects are combined level by level, and every conflicting scalar or array now carries the Override value — pretty-printed with 2-space indentation.

  5. 5

    Open it in the editor

    Send the merged JSON into the full editor to format it, validate it, explore it as a tree or mind map, or ask the AI copilot to explain a section.

How the merge works

Deep object merge

Nested objects combine level by level: app.log in the Base and app.log in the Override merge into one object instead of one side wiping out the other.

Override wins conflicts

When both sides define the same key with non-object values — port: 8080 vs port: 9090 — the Override value is kept. Deterministic, no prompts, no heuristics.

Arrays replaced whole

An Override array is the complete new list. No concatenation, no index-by-index splicing — the convention layered config systems follow.

null is a value, not a delete

An explicit null in the Override overwrites the Base value with null. The key stays — unlike JSON Merge Patch (RFC 7386), where null removes it.

Runs in your browser

Both documents are parsed and merged 100% client-side. Nothing is uploaded, so real configs with secrets never leave your machine.

Errors name the side

A malformed document is reported as "Base (left) input is not valid JSON" or the Override equivalent, so you know exactly which panel to fix.

Deep-merge semantics, precisely

The whole algorithm fits in three rules. When a key holds an object on both sides, the two objects merge key by key, and the same rules apply one level down. When a key exists on only one side, it is kept as is. In every other case — scalar vs scalar, array vs array, or mismatched types — the Override value replaces the Base value. There is no third state and no heuristic: the same two documents produce the same result every time.

The rules compose in useful ways. Type conflicts resolve wholesale: if the Base holds an object at a key and the Override holds a string, the string wins, and the merge never tries to reconcile across types. An explicit null in the Override counts as a value — it overwrites the Base entry with null but keeps the key. That is a deliberate difference from JSON Merge Patch (RFC 7386), where null means "delete this key". This tool applies overlay semantics, not patch-with-deletion semantics.

Two output details are worth knowing. The result is pretty-printed with two-space indentation, ready to paste into a config file. And key order is stable: the Base keys come first in their original order, with keys that only exist in the Override appended after them — which is why debug lands at the end of the example above.

Why arrays are replaced, not merged

Consider ["auth"] merged with ["auth", "cache"]. Concatenation yields ["auth", "auth", "cache"] — a duplicated flag. Index-by-index merging pairs elements by position and produces nonsense the moment the lists differ in order or length. No strategy is right for every document, because the merge cannot know whether your array is a set of flags, an ordered pipeline, or a list of records with identities. Replacement is the one strategy that never needs that knowledge: the Override list is the complete new list.

Config-layering tools reached the same conclusion. Kubernetes replaces arrays outright in a plain merge patch (RFC 7386) and merges lists element-wise only in strategic merge patches, where the API schema explicitly declares a merge key for the field. In the webpack ecosystem, merge helpers that concatenate arrays by default ship replace strategies precisely because appended loader and plugin lists are a classic source of duplicates. Wherever a schema is unavailable — which is exactly the situation for a generic JSON tool — replacement is the convention.

The practical guidance follows directly. To keep Base entries, repeat them in the Override array; the example does this with "auth". And if you genuinely need element-level merging, restructure the list as an object keyed by id or name — {"auth": {...}, "cache": {...}} — merge, and convert back. Deep merge then operates per key, which is usually what "merge these arrays" meant in the first place.

Where a JSON merge earns its keep

Environment overlays are the canonical case: a base.json holding the full default configuration, plus a tiny staging.json or production.json that defines only what differs. Deep-merging them produces the effective config for that environment — and because overlays stay minimal, a review of staging.json shows you exactly what staging changes and nothing else. When an app behaves differently in one environment, merging the layers by hand here lets you read the effective config directly instead of simulating the layering in your head.

Feature flags and tenant settings follow the same shape. A default flag set deep-merges with a per-plan or per-tenant override: nested groups combine, individual toggles flip, and the flags the override never mentions keep their defaults. The third recurring case is API defaults — merging caller-supplied options over a defaults object before building a request, the deep version of what Object.assign and object spread do shallowly.

All three cases share a property that matters for a browser tool: the documents are often sensitive. Configs carry connection strings and API keys; tenant settings carry customer names. This merge runs entirely client-side, so pasting a production config here does not send it anywhere.

Merge or diff? Two directions of the same question

Merge and diff are inverse tools. A merge combines forward: given a Base and an Override, it produces the effective document. A diff looks backward: given two documents, it reports what changed between them without touching either. If you are holding two configs and wondering how they differ, you want the JSON Diff tool; if you already know the delta you want to apply, you want the merge.

They pair well in sequence. Diff the Base against the Override before merging to preview which keys the overlay will touch. Merge. Then diff the Base against the merged result to verify that only the intended keys moved — a two-minute audit that catches a mistyped key in an overlay before it ships. Both tools work on parsed structure rather than text, so indentation and whitespace differences never pollute the comparison. And when the merged document is the one you will deploy, open it in the editor to validate it and walk the final structure as a tree.

Frequently asked questions

Is the JSON merge tool free?

Yes. Merging runs entirely in your browser with no account and no upload — both documents are parsed and combined on your machine, so configs containing secrets or customer data never leave it.

What happens when both documents define the same key?

It depends on the types. Two objects merge recursively, key by key. Anything else — two scalars, two arrays, or mismatched types — resolves to the Override value. The status line states it after every run: objects combined deeply; on conflicts the Override side wins.

Are arrays merged or replaced?

Replaced. An Override array is treated as the complete new list, matching how layered config systems handle lists — concatenation would duplicate entries and positional merging breaks when order or length differ. To keep Base entries, include them in the Override array too.

Which document wins conflicts — the first or the second?

The second. The top panel is the Base (your defaults); the panel below is the Override (your changes). Wherever both define the same non-object value, the Override is kept. If you meant the layering the other way around, swap which document goes in which panel.

Does setting a key to null in the Override delete it?

No. null is an ordinary value here: the key survives in the output with the value null. JSON Merge Patch (RFC 7386) behaves differently — there, null means "remove this key". If you need a key gone, delete it from the merged output in the editor.

Can I merge more than two JSON files?

Chain the merges: combine file 1 and file 2, then use the result as the new Base and merge file 3 over it, and so on. Order matters — later layers override earlier ones, exactly like a stack of layered config files.

What if one of the documents is not valid JSON?

The merge stops and the error names the side — "Base (left) input is not valid JSON" or "Override (right) input is not valid JSON" — so you know which panel to fix. The JSON Repair tool can clean up trailing commas, single quotes, and similar syntax problems first.

How is JSON merge different from JSON diff?

Merge combines two documents into one; diff reports how two documents differ without changing either. Use the diff to see a delta, the merge to apply one — and diff the Base against the merged result afterwards to double-check exactly what changed.

Related tools

Merge your JSON now

Open the Editor