JSON Diff

Paste two JSON documents. Every difference comes back with its full path.

Both documents are compared in your browser. Nothing is uploaded to a server.

What a JSON diff actually tells you

Text diff tools compare lines. JSON is not made of lines — it is a tree, and a pretty-printer can move every line without changing a single value. That is why a normal line diff of two API responses is usually unreadable: reformat the payload and you get a hundred red lines describing zero real changes. A structural diff parses both documents first, then walks the two trees together and reports only what the data says. Formatting, indentation and key order fall away.

This tool classifies every finding into one of three buckets. Added means the path exists in the changed document but not in the original. Removed is the reverse. Changed means the path exists on both sides with different values — including a type change, where a field went from a number to a string and quietly broke a consumer downstream.

Worked example: debugging an API response

Say a staging endpoint returns {"name":"Ada","role":"admin","tags":["a","b"]} and production returns {"name":"Ada","role":"editor","tags":["a","c"],"active":true}. Paste one into each box and the summary reads Added 1, Removed 0, Changed 2, with three rows:

Three lines, and the bug report writes itself. Compare that with scrolling two 400-line responses side by side hoping to spot a changed enum value.

How to read the paths

Paths are written the way you would type them in JavaScript or in a jq filter. Object keys are joined with a dot, array elements use square brackets with a zero-based index, and the two nest freely: user.orders[2].items[0].sku means the SKU of the first item of the third order. Copy the path straight into your console against the same object and you will land on the exact value the row is talking about. When the two documents differ at the very top — a bare number against an object, say — the path column shows (root), because there is no key to point at.

Why arrays behave differently from objects

In JSON, an object is an unordered bag of keys. Serialisers, hash maps and languages all reorder keys freely, so this tool treats an object as a set: reordering {"a":1,"b":2} to {"b":2,"a":1} produces no differences at all. Arrays are ordered by definition, so position is data. The comparison is index-based: element 0 against element 0, element 1 against element 1.

The practical consequence is worth internalising. Insert one entry at the top of a 50-item array and you will see roughly 50 changed rows plus one added row at the end, because everything shifted by one. That is not a bug, it is the honest answer to "is index 7 the same value in both documents?" If your array is really an unordered collection of records, sort both sides by a stable id before pasting, or diff the records one at a time. Many teams switch such payloads from arrays to keyed objects for exactly this reason.

Config drift between environments

The second big use case is configuration. Staging works, production throws a 500, and the two config files are meant to be identical apart from hostnames. Paste both in: the diff shows that featureFlags.newCheckout is true in staging and false in production, and that timeouts.readMs went from 3000 to 300 — a missing zero that no reviewer noticed. Type changes surface here too. A value quoted as "3000" in one file and left bare as 3000 in the other looks nearly identical on screen but is reported as changed, because a string and a number are genuinely different to whatever reads the file.

Practical tips

Keep the original on the left. The labels Added and Removed are relative to the left box, so swapping the two sides inverts the report and makes a rollback look like a release. If a document will not parse, the error names the side and repeats the parser's own message, which usually points at a trailing comma, a single quote or an unescaped newline inside a string — none of which are legal JSON even though most editors accept them.

Long values are truncated at 80 characters in the list so a base64 blob cannot push the path off screen; the counters and the classification still use the full value. And because both parsing and comparison happen in the page itself, you can paste a production response with real customer records without it ever leaving your machine — load the page once and it keeps working offline.

Sources & further reading

Frequently asked questions

Does key order matter in a JSON diff?

Not for objects. An object with a before b is the same object as b before a, so this tool reports no difference. Arrays are the opposite: order is part of the data, so [1,2] against [2,1] comes back as two changed items.

Why is a moved array item shown as changed?

The comparison is index-based: position 0 is compared with position 0, position 1 with position 1. Insert one item at the front and every later index shifts, so each one is reported as changed. That matches how parsers actually read the array, but when order is not meaningful you are usually better off comparing records by a stable id field.

Is my JSON uploaded anywhere?

No. Parsing and comparison both run in JavaScript inside this page, so nothing leaves your device. Load the page once, go offline, and it still works. That makes it safe for API responses or config files that contain customer data.

How large can the two documents be?

A few megabytes each is comfortable on a modern laptop, because the diff walks every node exactly once. What slows down first is the rendered list, so only the first 500 differences are drawn while the counters stay accurate. For files far bigger than that a command line diff is the better tool.