JSON Diff — Compare Two JSON Files

Changes
Filter:
Examples:

Last updated: 2026-09-17

Paste two JSON documents to see exactly what changed. The diff is semantic by default — key order does not matter. You get a change tree showing every added, removed, and modified path, plus a JSON Patch (RFC 6902) you can copy and a markdown report for pull requests. Everything runs locally in your browser.

What this tool does

Paste two JSON documents and get a semantic diff: every added, removed, and modified path, a JSON Patch (RFC 6902) you can apply programmatically, a markdown report for pull requests, and rendered HTML views for visual review. Key order is ignored by default. Arrays can be matched by index, by key field, or as unordered sets. Specific paths can be excluded with glob patterns. Everything runs locally in your browser — no data leaves your machine.

How to use

Paste left JSON

Paste your original or “before” JSON into editor A (left). You can also drag a file or use smart paste to auto-repair broken JSON.

Paste right JSON

Paste your modified or “after” JSON into editor B (right). Use the swap button to swap sides if needed.

Review the change tree

The default view shows a tree of every change with its path, type (added, removed, modified), and values. Click any change to jump to it.

Switch views or export

Switch to Side-by-side, Patch (RFC 6902), or Summary view. Export a markdown change report or copy the JSON Patch.

Example

Given a before document:

{
  "id": "order-42",
  "status": "pending",
  "total": 99.90,
  "items": [
    {"id": "sku-1", "qty": 2, "price": 49.95},
    {"id": "sku-2", "qty": 1, "price": 0}
  ],
  "shipped": false
}

And an after document:

{
  "id": "order-42",
  "status": "shipped",
  "total": 99.90,
  "items": [
    {"id": "sku-1", "qty": 1, "price": 49.95},
    {"id": "sku-3", "qty": 1, "price": 12.50}
  ],
  "shipped": true,
  "tracking": "1Z999"
}

With array matching set to by-key (using the auto-detected id field), the change tree reports:

The /id and /total paths are absent because their values did not change. The second array element is reported as a remove plus an add because sku-2 was replaced by sku-3, not merely reordered. If the items were reordered without any new or removed SKUs, key matching would report moves instead.

Diff views explained

The tool produces four primary output formats, each suited to a different stage of review:

Tree view (HTML tree) — The default output. Every changed path renders as a collapsible node showing its old and new values side by side. Nodes are color-coded: green for additions, red for removals, amber for modifications. Nested objects and arrays can be expanded or collapsed so you can drill into a specific branch without scrolling through unrelated changes. Use this when you need to audit individual field-level changes, trace a regression to a single property, or present a reviewable hierarchy to a teammate unfamiliar with the raw JSON.

Side-by-side view (HTML) — Renders both documents formatted and syntax-highlighted, with changed lines marked in both panes. This is the view closest to a traditional code diff tool. The rendering is virtualized, so documents with thousands of lines remain scrollable without freezing the tab. Use this when you want a visual scan of the overall shape of the changes.

Patch view — Emits a JSON Patch array conforming to RFC 6902. Each entry is an operation — add, remove, replace, move, copy, or test — with a JSON Pointer path and a value where applicable. Copy the patch to your clipboard and apply it with any RFC 6902 library in Node, Python, Go, Rust, or any other language with a patch implementation. Use this when you need to transmit or persist a set of changes, or when you want to apply the same delta to multiple base documents.

Summary / markdown report — A compact text output listing the count of additions, removals, and modifications, followed by a markdown bullet list of each changed path with its old and new values. Paste this directly into a pull request description, commit message, or release note. Use this when you need a human-readable changelog rather than machine-readable patch operations.

Pick the view for your audience. The tree view suits an engineer auditing a regression; the patch view suits a downstream consumer applying the delta to another baseline snapshot; the summary view suits a reviewer scanning a pull request without toggling into verbose internals. You can switch views at any time without re-running the diff, because the underlying delta is computed once and every view is just a distinct rendering of that same intermediate result.

Array matching modes

Arrays are the hardest part of JSON diffing because the same logical item can appear at different positions in the two documents. The arrayMatch option controls how elements are paired before comparison:

By index (default) — Elements at the same array position are compared. Comparing [A, B] with [A, C] produces a single change at index 1. This is the right choice when array position is semantically meaningful — for example, a fixed-length coordinate pair like [lat, lng], or an ordered pipeline of processing steps where each slot has a specific role.

By key — Elements are matched by the value of a field within each object. The tool auto-detects the key field by checking for id, then _id, then uuid, then key, then name, in that order, using the first field that exists on the array elements. You can override this with the arrayKeyField option to use any other field. When two arrays contain the same objects in a different order, key matching reports moves rather than remove-plus-add pairs, which keeps the diff small and readable. Use this for arrays of objects that carry stable identifiers — line items in a shopping cart, rows in a database table, or entries in a feed.

As unordered set — Order is ignored completely. [A, B] and [B, A] are treated as identical. The diff reports only items that were added or removed, never positional changes. Use this for tag lists, permission sets, role assignments, or any collection where membership is the only thing that matters and sequence is irrelevant.

When array elements are objects and none of them expose any detectable key field, the by-key and set modes still function by falling back to comparing each element’s content as a whole. For key mode this means two objects with identical content are treated as the same row. For set mode, membership is decided by deep object equality rather than a field value, which works but is slower for large arrays and can misclassify near-duplicates if your rows differ only in a field you intended to ignore.

Ignoring paths

The ignorePaths option accepts glob patterns. Two wildcards are supported: * matches any single path segment, and ? matches a single character within a segment. Every node in the diff is checked against your patterns, and matched nodes are excluded entirely — they will not appear as added, removed, or modified.

Common patterns:

Separate multiple patterns with commas. Patterns are matched against the full dot-separated path of each node, so a.b.c targets a field c nested inside b inside a at the root level. Use this to strip out volatile fields like timestamps, hash values, request IDs, or cache entries that change on every request and would otherwise clutter the diff with noise. Note that ignoring a parent node also hides every descendant beneath it, which is usually what you want for an opaque blob like a signed token or a serialized payload you treat as a single unit.

Loose type comparison and float tolerance

Two options handle mismatches that strict equality would flag as changes but that are usually not meaningful:

looseTypeComparison — When enabled, the string "1" is treated as equal to the number 1, "42" equals 42, and "true" equals the boolean true. The comparison normalizes stringified scalars to their typed counterparts before checking equality. This is useful when one document came from a system that stringifies everything — URL query string parsers, certain CSV import pipelines, or older API responses that wrap all values in quotes. Disable this option when a type change is itself a meaningful difference you want the diff to surface, such as a field migrating from a string code to a numeric enum.

floatTolerance — Rounds floating-point numbers to a specified number of decimal places before comparing them. Without this, 0.1 + 0.2 produces 0.30000000000000004 in JavaScript, which would show up as a change from 0.3 to 0.30000000000000004 — a phantom difference caused by IEEE 754 representation error, not a real data change. Set the tolerance to the number of decimal places your values actually carry. Use this when comparing calculated totals, geographic coordinates, averages, or any value that has passed through floating-point arithmetic.

Do it in code

JavaScript, using jsondiffpatch — the same library this tool uses internally:

import { diff, formatters } from 'jsondiffpatch';

const left = { status: 'pending', total: 99.90, shipped: false };
const right = { status: 'shipped', total: 99.90, shipped: true };

const delta = diff(left, right);
// { status: ['pending', 'shipped'], shipped: [false, true] }

const patch = formatters.jsonpatch.format(delta);
console.log(JSON.stringify(patch, null, 2));
// [{"op":"replace","path":"/status","value":"shipped"},
//  {"op":"replace","path":"/shipped","value":true}]

Python, using the deepdiff library for diffing and jsonpatch for applying patches:

from deepdiff import DeepDiff

left = {"status": "pending", "total": 99.90, "shipped": False}
right = {"status": "shipped", "total": 99.90, "shipped": True}

result = DeepDiff(left, right, ignore_order=True)
print(result)
# {'values_changed': {"root['status']": {'new_value': 'shipped', 'old_value': 'pending'},
#                     "root['shipped']": {'new_value': True, 'old_value': False}}}

Applying a JSON Patch in Python:

import jsonpatch

doc = {"status": "pending", "total": 99.90}
patch = [{"op": "replace", "path": "/status", "value": "shipped"}]
result = jsonpatch.apply_patch(doc, patch)
# {'status': 'shipped', 'total': 99.9}

Common mistakes when diffing

Edge cases handled

Frequently asked questions

What is semantic JSON diff?
Semantic diff compares the meaning of two JSON documents, not their text. Key order does not matter: the objects {a:1,b:2} and {b:2,a:1} are identical. This is the default because in most real-world JSON such as API responses and config files, key order is not significant.
When should I use text diff instead?
Use text diff (a standard line-by-line comparison) when key order matters — for example, when comparing serialized files where the order is part of the format, or when you want to catch whitespace-only changes. This tool does semantic diff; for text diff, use a standard diff tool.
How do I ignore timestamps or metadata?
Use the Ignore paths option with glob patterns. For example, the pattern *.updatedAt ignores any field named updatedAt at any nesting level, and meta.requestId ignores a specific nested path. Multiple patterns are comma-separated.
What is JSON Patch (RFC 6902)?
JSON Patch is a standard format for describing changes to a JSON document. It is an array of operations: add, replace, remove, move, copy, and test. You can copy the patch and apply it programmatically in any language that has a JSON Patch library.
How does array matching work?
By default, arrays are compared element-by-element by index. Switch to by-key mode to match array elements by a field like id, which is useful when array items are reordered. Use unordered-set mode to treat arrays as sets where order does not matter at all.
Can I compare large JSON files?
Yes. Parsing and diffing run in a Web Worker for inputs over 200KB so the UI stays responsive. The side-by-side view is rendered efficiently and can handle large documents.
What does loose type comparison do?
When enabled, it treats the string 1 as equal to the number 1, and the string true as equal to the boolean true. This helps when one document was serialized through a system that stringifies all values, such as a URL query string parser. Disable it when type changes are meaningful differences you want to catch.
Can I diff JSON that has duplicate keys?
JSON.parse keeps the last value for each duplicate key, so the diff sees only the final value. Fix duplicate keys in your source data before comparing. The tool does not warn about duplicates because it relies on the browser native parser.

Related tools