JSON Diff
Compare two JSON documents and see exactly which fields were added, removed, or changed
JSON A (left / before)
JSON B (right / after)
JSON Diff
Compare two JSON documents and see exactly which fields were added, removed, or changed
Features
- Structural diff between any two JSON values — objects, arrays, and primitives are all recursed properly (null is treated as a scalar, not an object, fixing the typeof-null bug)
- Each difference is reported as an RFC 6901 JSON Pointer path (e.g. `/users/0/email`) so you can navigate straight to the exact node in deeply nested data
- Modified entries show before AND after values side-by-side, so it's obvious whether 'active' flipped true→false or whether a number changed
- Summary counts: added, removed, modified, and unchanged leaves — at-a-glance scale of the diff before reading the per-path detail
- Per-side parse-error reporting names which input failed and includes the JavaScript engine's parse message, so you don't waste time guessing whether A or B is malformed
- Copy report button emits the full diff (grouped Added / Removed / Modified sections plus unchanged count) as plain text — paste into a code review or commit message
- Load Sample fills both panes with a tiny realistic example (user record with role + address changes) so first-time users see the diff working without typing
- Pure client-side: JSON A and JSON B are parsed and compared in your browser; nothing is uploaded, and the tool works offline after the page loads
How to use
- Paste your 'before' JSON in the left textarea (JSON A) and the 'after' JSON in the right (JSON B).
- Click Compare. The diff is computed recursively; objects are diffed by key, arrays by index.
- Read the summary counts: how many keys were added, removed, modified, and how many leaves matched.
- Scroll the per-path lists to see exactly which JSON Pointer paths changed; modified rows show before → after values.
- Click Copy report to grab the whole diff as plain text — useful for review notes, bug reports, or commit messages.
- Use Load Sample to see a working example before pasting your own data.
Tips & Best Practices
- Run JSON Formatter on both sides first if you want a textual diff to also show structurally-equal documents as no-change.
- For large arrays where order matters, this tool is exactly right; for sets, pre-sort by a stable key before comparing.
- The path output is RFC 6901, so you can hand it directly to libraries like fast-json-patch to apply changes programmatically.
- Copy the report into PR descriptions to show reviewers exactly what changed in a config or fixture JSON.
- If parse errors point to a specific column, the JavaScript engine's message often includes line/column from json5-style parsers — but here we use strict JSON.parse, so line/column comes from the host.
FAQ
How are arrays compared — by index or by value?
By index. Element 0 of A is compared to element 0 of B, 1 to 1, and so on. If A is longer, the extra tail indices are reported as Removed; if B is longer, the extra tail is Added. This is fast and predictable but means a single-element insert at the start of a big array will cascade into many 'modified' entries. For order-insensitive comparisons (e.g. sets of unique IDs), sort both arrays first or pre-process into objects keyed by the identifier.
What's the path notation in the result?
RFC 6901 JSON Pointer: paths are slash-separated tokens that descend into the JSON tree. `/users/0/email` means 'the email property of element 0 of users'. Keys containing `~` are escaped as `~0`, slashes as `~1`. The root itself is shown as `/`. This is the same path notation used by patch tools, JSON Schema instancePath, and the json-formatter pointer hover.
How is null handled?
null is treated as a leaf value, not as an object — even though typeof null === 'object' in JavaScript. So {a: null} vs {a: {}} shows as modified rather than recursing into an empty object. {a: null} vs {a: null} counts as one unchanged leaf, and {a: null} vs {a: 0} shows as modified with before=null after=0.
Does the diff handle keys with special characters?
Yes. Keys containing dots, slashes, brackets, or tildes are JSON-Pointer-escaped in the path output — `~` becomes `~0`, `/` becomes `~1`. So a key literally named `a/b` shows up as `/a~1b` in the path, unambiguously reversible.
Why does the unchanged count not match my expectation?
Unchanged is counted at the LEAF level — primitives that compare equal between A and B. A deeply nested object whose subtree is entirely identical contributes one unchanged entry per leaf, not one for the whole subtree. This makes the count a true sanity check: 'how many primitive values stayed the same'.
What if one side is null or empty?
Empty input parses as null (so `{a:1}` vs empty compares against null and reports the whole object as added). A literal `null` is a valid JSON document; comparing two nulls yields zero added/removed/modified and one unchanged. If both inputs are empty, the diff trivially reports zero everywhere.
Is my JSON sent anywhere?
No. Both textareas stay in your browser; the diff runs in the same JavaScript context that rendered the page. The Network tab in DevTools shows no requests when you press Compare. The tool works offline after the page is cached.
How is this different from a text-line diff?
A text diff like `diff a.json b.json` operates on characters and line breaks: reformat the same data and it lights up. This is a STRUCTURAL diff: `{"a":1, "b":2}` and `{"b":2, "a":1}` are identical (zero diff). It also doesn't care about indentation, trailing whitespace, or comment styles. Use a text diff when you care about formatting; use this when you care about the data.