JSON Diff

Compare two JSON documents and see exactly what changed. Semantic diff — object key order is ignored. Outputs human-readable summary, JSON Patch (RFC 6902), or JSON Merge Patch (RFC 7396). Runs entirely in your browser; data never leaves your machine.

Developer Tools
Ad
JSON A (original) empty
JSON B (updated) empty

What this tool does

Paste two JSON documents — left side is the original, right side is the updated version — and the tool computes a structural diff between them. The result is shown three ways: a human-readable summary that color-codes additions, removals, and changes; a JSON Patch (RFC 6902) document that can be applied programmatically to convert A into B; and a JSON Merge Patch (RFC 7396) document, which is the simpler alternative useful for sparse updates over HTTP PATCH.

The diff is computed entirely in your browser — your JSON is never sent to a server, never logged, never persisted. That matters if you're diffing production API responses, internal configs, or anything else you wouldn't want sitting in a third party's request log.

Semantic diff vs textual diff

A plain text diff (the kind git diff produces) treats JSON as a stream of characters. Reorder the keys of an object, and the textual diff lights up like a Christmas tree even though the two documents represent identical data. JSON has no concept of key order — {"a":1,"b":2} and {"b":2,"a":1} are the same object, byte-for-byte different but semantically equal.

A semantic diff understands the structure. Object comparison ignores key order. Number comparison handles the float-vs-integer ambiguity (1 and 1.0 are equal). String comparison is exact (whitespace matters). Array comparison is positional by default — [1,2,3] is different from [3,2,1] — because most JSON arrays carry meaningful order.

This tool produces a semantic diff. If you specifically need a textual diff for a JSON file (rare, but happens when you're debugging serialization issues), pretty-print both sides with the JSON formatter and run a regular diff tool.

JSON Patch (RFC 6902)

JSON Patch is a standardized format for describing changes to a JSON document. The output is itself JSON — an array of operations, each with an op field (add, remove, replace, move, copy, or test) and a path expressed as a JSON Pointer (RFC 6901). This tool produces add, remove, and replace operations — sufficient to express any diff.

[
  { "op": "replace", "path": "/user/email", "value": "alice@new.com" },
  { "op": "add",     "path": "/user/verified", "value": true },
  { "op": "remove",  "path": "/status" }
]

The format is supported by libraries in essentially every language — fast-json-patch in JavaScript, jsonpatch in Python, github.com/evanphx/json-patch in Go. Apply the patch document to the original JSON, get the updated JSON. Useful for HTTP PATCH endpoints, undo/redo systems, optimistic UI updates, and CRDT-adjacent state synchronization.

JSON Merge Patch (RFC 7396)

JSON Merge Patch is the simpler alternative. Instead of an operation list, you provide a partial JSON document that gets merged into the target. Fields set to null mean "delete this key". Everything else means "set this key to this value".

{
  "user": {
    "email": "alice@new.com",
    "verified": true
  },
  "status": null
}

The trade-off is that Merge Patch can't express "set this field to literal null" (because null means delete), and it can't surgically modify a single element of an array — any array change replaces the whole array. If those limitations don't matter for your use case, Merge Patch is simpler and more human-readable than JSON Patch. RFC 7396 is what most modern HTTP APIs use when they say "PATCH endpoint".

Practical workflows

  • API changelog detection. You upgraded a third-party library and want to know exactly what the response shape changed. Run a request against both versions, paste the responses, see precisely what shifted.
  • Config migration auditing. Your terraform.tfstate or k8s manifest changed between commits. The text diff is noisy; the semantic diff shows only the structurally meaningful changes.
  • Snapshot test debugging. Jest told you a snapshot diverged. The error message is hard to read. Paste both snapshots here for a clearer summary.
  • HTTP PATCH request building. You have the current state and the desired state; the API wants a JSON Merge Patch in the request body. Generate it here, paste it into your request.
  • Comparing API responses across environments. Staging returns one shape, production another. Diff them to find what's drifting.
  • Detecting unintentional changes. Code review tool says one line changed in a 5,000-line JSON config. Paste before/after to see what was actually edited semantically — sometimes it's nothing (just reformatting).

Array diff gotchas

JSON arrays are the trickiest case. The diff engine compares element-by-element at the same index. If you insert one element at the start of an array, the engine sees it as: every subsequent element changed. That's technically correct (positional changes really did happen) but rarely what humans want to see.

A: [1, 2, 3]
B: [0, 1, 2, 3]
Output: /0 replaced with 0, /1 replaced with 1, /2 replaced with 2, /3 added

A smarter longest-common-subsequence diff would report "0 inserted at index 0" and leave the rest alone. Most production-grade JSON Patch libraries (including fast-json-patch) use the simple positional algorithm because LCS-based array diff is dramatically slower and the output isn't always cleaner. If your use case really benefits from the LCS approach — typically only when arrays of strings or numbers are reordered frequently — you'll want a dedicated library and shouldn't paste through this tool.

Common pitfalls

  • Number precision. 0.1 + 0.2 === 0.3 is false in IEEE 754, which means a diff between {"x":0.3} and {"x":0.1+0.2} shows a difference (0.3 vs 0.30000000000000004) even though they look "the same". JSON gives you raw floats; round to your desired precision before serializing.
  • Number vs string IDs. Many APIs return numeric IDs as JSON strings ("id": "12345") because JavaScript can't safely represent integers above 2^53. Comparing {"id": 12345} against {"id": "12345"} shows a type change — same logical value, different JSON type.
  • Null vs missing. {"x": null} and {} are different. The first has a key x with value null; the second has no key at all. The diff treats them as a removed key.
  • Trailing whitespace in strings. "hello" and "hello " are different. Strip whitespace before diffing if that matters for your use case.
  • Object key order in the output. The diff tool preserves whatever order the input has. If both inputs are pretty-printed with different formatters, the textual output of the diff might look reordered. The semantic conclusion is unaffected.

FAQ

See the FAQ section below for answers to: privacy of pasted data, maximum input size, how the diff handles arrays of objects, why your "obvious" patch looks different from what the tool produces, and how to apply the generated patches programmatically.

Common use cases

Frequently asked questions

Does the JSON I paste go to a server?

No. The diff runs entirely in your browser using local JavaScript. Your data never crosses the network, isn't logged anywhere, and isn't persisted. You can confirm by opening DevTools → Network tab and pasting — there will be no request when the diff runs.

Why does reordering object keys not show as a difference?

Because JSON objects are unordered by specification. <code>{"a":1,"b":2}</code> and <code>{"b":2,"a":1}</code> represent the identical data. The semantic diff (which this tool does) correctly reports them as equal. If you specifically need a text-level diff that catches reordered keys, pretty-print both sides and run a regular text diff.

Why does inserting one item at the start of an array show every later item as changed?

Array comparison is positional. The element at index 0 changed (from old[0] to new[0]); the element at index 1 changed (from old[1] to new[1]); and so on. RFC 6902's reference implementation works the same way. A longest-common-subsequence algorithm would report this more naturally as "1 item inserted at index 0", but it's significantly slower and not always cleaner. For typical config and API-response diffs, positional comparison is correct.

Difference between JSON Patch and JSON Merge Patch?

JSON Patch (RFC 6902) is an array of operations — add, remove, replace, move, copy, test — each with a JSON Pointer path. Powerful, can express any change, supported by libraries in every language. JSON Merge Patch (RFC 7396) is a partial document where present keys mean "set" and null values mean "delete". Simpler but can't express "set this field to literal null" and replaces whole arrays atomically. Most HTTP PATCH endpoints accept Merge Patch.

How do I apply the generated patch programmatically?

JavaScript: <code>fast-json-patch</code> from npm. Python: <code>jsonpatch</code> from PyPI. Go: <code>github.com/evanphx/json-patch</code>. All take the original document and the patch array and return the updated document. JSON Merge Patch is even simpler — most languages have a built-in or one-function library.

My patch looks bigger than expected — why?

Most common cause: an array reordering or insertion that you perceive as "one change" actually requires multiple positional ops to express. If the patch must be minimal-size, run an LCS-based array-diff tool first. If the patch just needs to be CORRECT (it can be applied to A to produce B), the current output is fine and arguably more debuggable.

Maximum input size?

~2 MB per side. Beyond that the in-browser engine slows down. For very large JSON, you typically want a streaming approach anyway — try jq or a dedicated diff library locally.

What about JSON with comments or trailing commas?

Strict JSON only — comments and trailing commas aren't allowed by the spec, and the parser rejects them. If your input is JSONC (VS Code config style) or JSON5, strip the comments first or convert via a converter tool.

Related tools