JSON Diff Viewer
Compare two JSON documents semantically. Detects added, removed and changed keys and array elements; ignores object key order.
Reviewed by Aygul Dovletova · Last reviewed
How to use the JSON Diff Viewer
- Paste the left (original) document into the first textarea. Any valid JSON works: an API response, a configuration file, a snapshot fixture, or a single primitive.
- Paste the right (changed) document into the second textarea. The two sides can have any structure; the viewer compares whatever it finds.
- Press Compare. The summary panel surfaces counts of added, removed, and replaced entries; the detailed list below shows one row per op with the operation kind, a JSONPointer path, and the old and new values where applicable.
- Reorder object keys in either side and re-run. The output stays the same because object key order is not a JSON-level difference; this is the killer feature versus a plain text diff.
- Copy the path of any op to feed it into
jq,fast-json-patch, or any other RFC 6901 consumer.
What the viewer does under the hood
Both inputs are parsed with native JSON.parse, which catches malformed JSON before the comparator runs. The diff function walks both trees in parallel inside JsonDiff.logic.ts: object keys match by name (an add when the right side has a key the left lacks, a remove when the inverse, a replace when both sides have the key but values differ), array elements match by index in document order. Primitive values use strict equality plus a deep-equal helper for nested arrays and objects so that re-ordered sub-objects do not register as changes.
Output paths follow RFC 6901 (JSONPointer) exactly. The root is /, child keys are appended after a slash, and the special characters ~ and / are escaped to ~0 and ~1 so that any string can become a valid pointer. This format is the one consumed by RFC 6902 JSON Patch documents and by every JSON Patch library on npm or PyPI, so the diff output is portable into a real patch pipeline without extra parsing. The whole comparator is a pure function with unit-test coverage and zero runtime dependencies.
A worked example: reading the ops
Suppose the left (original) payload is a user record and the right is the same record after an edit:
// left // right
{ {
"id": 42, "id": 42,
"name": "Ada", "name": "Ada Lovelace",
"roles": ["author"], "roles": ["author", "admin"],
"legacy": true "verified": true
} }
Compare produces four ops, each with a JSONPointer path:
replaceat/name, old"Ada", new"Ada Lovelace"- same key, new value.addat/roles/1, new"admin"- an array element appended at index 1.removeat/legacy, oldtrue- key present only on the left.addat/verified, newtrue- key only on the right.
Notice that /id produces no op because its value is unchanged, and reordering name and id would not change the output. That stability under key reordering is what makes the diff usable on config files and API snapshots where authors shuffle keys freely.
When this tool earns its keep
- Reviewing what changed between two captures of the same API response: yesterday's payload versus today's, or staging versus production.
- Diffing two configuration files (Kubernetes manifests, Terraform plan output, OpenAPI documents) where authors reorder keys casually and a text diff produces 80% noise.
- Spotting an unexpected field that drifted into a Vitest or Jest snapshot fixture during a refactor that shouldn't have changed the shape.
- Comparing two GitHub Actions workflow runs' final job output JSON to identify which step changed an output value.
- Auditing a database migration's effect on a serialized JSON column by exporting before and after rows and pasting them in.
- Producing a JSON Patch path list to hand to a downstream patch consumer (a CRDT replicator, a Yjs sync layer, a simple webhook delta forwarder).
Common pitfalls and edge cases
- Array alignment is index-based, not LCS. Inserting one element at the start of an array shifts every subsequent index and surfaces as a chain of replacements plus one add. This is the correct semantic for index-keyed data; for content-keyed lists, sort both sides first or use
jsondiffpatch. - Number-vs-string distinction matters.
{"id": 42}and{"id": "42"}register as a replace because JSON preserves the type. Many APIs flip this casually between versions, and the viewer surfaces the change you might miss in a text diff. - NaN, Infinity, and undefined are not JSON values; both inputs must be RFC 8259 valid. Strip them before pasting.
- Long strings with embedded JSON are not unwrapped. If a field stores a JSON string (a common API misdesign), the viewer compares the full quoted string and you see a single replace op, not a structural diff.
- Whitespace inside string values is significant.
"hello world"and"hello world"(one space vs two) are different values; this is JSON-correct but surprises engineers used to lossy diffs. - The viewer does not emit RFC 6902 patch documents directly. The output is structurally similar but uses friendlier op names; pipe through
fast-json-patchif you need the canonical patch format.
JSON Pointer (RFC 6901) and JSON Patch (RFC 6902)
JSON Pointer (RFC 6901, 2013) defines a single-line string syntax for naming any value in a JSON document: a sequence of slash-prefixed reference tokens, with ~ and / escaped. JSON Patch (RFC 6902) builds on Pointer to specify a list of add, remove, replace, move, copy, and test ops as a JSON document itself, designed to be applied to a target document atomically. The diff viewer's output is conceptually a subset of RFC 6902: it emits add, remove, and replace ops with paths, but does not produce the formal patch document. Wrapping the ops list in [] and renaming kinds gives you a valid Patch.
Alternatives and when they beat this tool
The CLI tool jd (Go) produces colorized terminal output with native YAML and JSON support and is the right pick for batch jobs. fast-json-patch on npm computes RFC 6902 patches programmatically and is what most JS server-side diffs use. jsondiffpatch (npm) does LCS-style array alignment and emits HTML diffs for human review. Python's deepdiff (PyPI) is the equivalent for backends in that language. The on-page viewer wins when you have two payloads in your clipboard, want a structured comparison without installing tooling, and do not want to upload sensitive data to a remote service like jsondiff.com.
Frequently Asked Questions
How is this different from a text diff?
A text diff like <code>diff -u</code> is line-aware - reordering keys or reformatting whitespace produces noisy false-positive changes. The JSON diff viewer parses both sides and compares values structurally, so <code>{"a":1,"b":2}</code> and <code>{"b":2,"a":1}</code> are equal.
Which paths does the viewer use?
JSONPointer (RFC 6901). Each diff op carries a path like <code>/user/name</code> that you can paste into <code>jq</code>, fast-json-patch, or any RFC-6901 consumer. <code>/</code> on its own represents the root document.
How are arrays compared?
Element-by-element by index, in document order. If the right side has additional elements they appear as <code>add</code> ops; missing ones appear as <code>remove</code> ops at the corresponding index. The viewer does NOT attempt LCS-style alignment - inserting one element at the start of an array will therefore look like a chain of replacements plus one add, which is the correct semantic for index-keyed data.
Does the viewer upload my JSON?
No. Both documents are parsed and compared inside your browser tab. There is no fetch call, no analytics beacon for the document content. Verify in DevTools by opening the Network tab and pressing Compare.
How are object keys with slashes shown?
JSONPointer escapes <code>~</code> as <code>~0</code> and <code>/</code> as <code>~1</code>. A change at <code>{"a/b": 1}</code> -> <code>{"a/b": 2}</code> shows as path <code>/a~1b</code>, which is the literal RFC 6901 form.
What counts as a replace versus an add plus a remove?
When both sides have the same key (or array index) but different values, the viewer emits a single <code>replace</code> op carrying the old and new value. An <code>add</code> means the key exists only on the right; a <code>remove</code> means it exists only on the left. A key moved to a different parent shows as a remove plus an add, since the viewer does not track moves.
Are nested objects compared deeply?
Yes. The comparator recurses into nested objects and arrays, so a change three levels down surfaces as one op at that deep path, not a replace of the parent. Identical sub-objects with reordered keys are reported as equal, the point of a semantic diff.
Does the order of the two documents matter?
It defines the direction. The left pane is the original, the right is the changed version, so swapping them turns every <code>add</code> into a <code>remove</code> and vice versa, while <code>replace</code> ops flip old and new values. Put the baseline on the left so diffs read like a changelog.
Can I diff two large JSON files in the browser?
Comfortably up to a few megabytes per side. Both documents are parsed and walked in a single pass, so the tab holds two trees plus the op list at once. For very large payloads, sample the relevant subtree or use a streaming CLI such as <code>jd</code> or <code>jq</code>.
How do I turn the output into a real JSON Patch?
Wrap the op list in <code>[]</code>, rename the kinds to the RFC 6902 verbs (<code>add</code>, <code>remove</code>, <code>replace</code>), and keep the paths as-is. For a canonical patch, feed both documents to <code>fast-json-patch</code> on npm, which emits the exact RFC 6902 form.
More Developer Tools
Base64 Encoder & Decoder
Encode UTF-8 text to Base64 online or decode Base64 back to UTF-8 and plain text. Runs in your browser with no upload.
Open toolBulk URL Encode / Decode
Encode or decode many URLs at once. Paste a newline-separated list and the tool processes each line in parallel, preserving order and blank lines.
Open toolCode Screenshot
Create beautiful code snippet images with customizable themes.
Open toolColor Converter
Convert colors between HEX, RGB, HSL and CMYK formats.
Open toolCron Expression Parser
Parse cron expressions into human-readable schedules with next run times.
Open toolCSS Formatter / Minifier
Format, beautify and minify CSS code.
Open tool