Nifty Tools

JSON diff

Run a json diff in your browser. Compare two JSON values structurally and read the change list by path. Pure JSON.parse, JSON Pointer paths, no upload.

Processing mode: Local Browser-local

Paste JSON on both sides to compare.

How to use it

JSON Diff — Compare Two JSON Values, In Your Browser

  1. Paste the original JSON on the left and the changed JSON on the right (or load `.json` files up to ~10 MB each). Both sides parse independently — if either side has invalid JSON, the status bar tells you which.
  2. Click Compare. The walker descends both trees in parallel, emits a row per difference at its JSON Pointer path, and produces a summary count of `added` / `removed` / `changed` / `type_changed`.
  3. Read the rows directly, or copy them as a JSON array of `{path, type, left, right}` objects for downstream tooling. Nothing leaves your browser.

Good for

Common use cases

People diff JSON when two payloads should be the same — and aren't. The classic situations are an API response that "broke" between releases (the contract is the same on paper but a flag flipped, a default changed, a field is now nested), a config snapshot from staging compared against production before a deploy, a webhook capture before and after a feature flag flipped, two `package.json`/`composer.json`/`pubspec.yaml` exports during a dependency upgrade, two dashboard or report exports from a SaaS where the JSON is the only audit trail, and partner-API regression checks where the contract has version markers but the contract body is the bit you actually care about. Doing this in a terminal works (`diff` on `jq -S` output, `python -m json.tool` piped into `diff`, `dyff` for YAML-shaped JSON) but a terminal-first workflow forces a context switch out of whatever tab held the original payload, hides the field-by-field semantics behind a line-noise comparison, and treats whitespace and key order as differences when they are not. The tool here parses both sides with `JSON.parse`, walks the data structurally, and emits a path-keyed change list so you can read "here, at `/users/0/email`, the value changed from `a@example.com` to `b@example.com`" instead of decoding a coloured line-diff. Doing it browser-local matters because the JSON in front of you routinely contains access tokens, account identifiers, customer PII, internal hostnames, and unreleased feature flags that have no business being pasted into an arbitrary online JSON diff service.

Processing mode

Browser-local

Files are processed by your browser. They never reach our servers.

Questions

JSON Diff — Compare Two JSON Values, In Your Browser FAQ

Why don't reordered object keys show up as changes?

Because they are not part of the JSON data model. After `JSON.parse`, `{"a":1,"b":2}` and `{"b":2,"a":1}` are the same object — JavaScript engines preserve insertion order for string keys (ECMAScript 2020+) but the JSON spec itself does not promise any order, and every consumer that reads the parsed value gets the same answer regardless of how the source happened to be authored. Treating reordered keys as changes would mean the tool reported as "different" two payloads that no JSON consumer can distinguish, which is the kind of false positive that conditions reviewers to ignore the diff.

Why do array changes look so noisy when I added one item at the start?

Because we compare arrays by index, not by identity. Inserting `"NEW"` at position 0 of `["a","b","c"]` gives `["NEW","a","b","c"]`, and under index comparison position 0 is now `"NEW"` instead of `"a"`, position 1 is `"a"` instead of `"b"`, position 2 is `"b"` instead of `"c"`, and position 3 is `"c"` (added). This is honest about what we did — index comparison is fast, deterministic, and produces the same answer every time. Move-aware diffs (LCS, Myers, Hungarian-algorithm matching) take a position on what counts as a "moved" element vs what counts as a "removed-and-re-added" element, and that decision is application-specific. If you need moves detected, the right tools are `jsondiffpatch` (browser-friendly, emits its own delta format) or `deep-diff` / `microdiff` in Node — those let you describe the array's identity contract first and then ask for a diff under that contract.

What does `type_changed` mean and why doesn't it descend into the new shape?

A `type_changed` row says "the kind of value at this path swapped" — a number became a string, a string became `null`, an object became a primitive, an array became an object. The two subtrees are no longer comparable in any meaningful way: descending into the new shape would emit one `added` row for every leaf the new shape has and one `removed` row for every leaf the old shape had, which is correct in a pedantic sense but misleading in practice — the change reviewers care about happened at the boundary, not at the leaves. We emit one row at the swap and stop. If you want the leaf enumeration, paste each side into a JSON formatter and read it manually; the cases where this matters are rare enough that we did not add a "show subtree on type_changed" toggle.

Are duplicate keys in a JSON object detected?

No, and they cannot be detected after `JSON.parse`. The JSON spec leaves duplicate-key behaviour "implementation-defined," and every browser we support (Chromium, Firefox, WebKit) keeps the LAST occurrence of a duplicated key and silently discards the earlier ones. By the time the diff walker sees the parsed value, the duplicates are gone — there is no API to ask the parser for them. If duplicate-key detection matters for your workflow, the right approach is a streaming parser (`json-source-map`, `JSON5` for relaxed grammar, `parse-json` for line/column-aware errors) on top of the raw text, before the data structure is built. This tool is built on the parsed object, on purpose, because the parsed object is what every downstream consumer of the JSON actually sees.

Are paths stable, and what does the format look like?

Yes — the path format is JSON Pointer (RFC 6901). The root is the empty string. Top-level keys are `/key`. Array indices are decimal integers without a leading zero (`/items/0`, never `/items/00`). Object keys that contain `/` or `~` are escaped per the RFC: `/` becomes `~1`, `~` becomes `~0`. So a key like `a/b` reads as `/a~1b`, and a key like `c~d` reads as `/c~0d`. The tool emits the same path format that an HTTP PATCH (RFC 6902) consumer, a JSON Schema `$ref`, or a `jsonpointer` library would use, so you can paste a returned path into any pointer-aware reader and get the same value.

How does this compare to `diff` on pretty-printed JSON?

A textual `diff` on the output of `jq -S` (sort keys then pretty-print) gets you most of the way for small payloads — the sort-keys flag fixes the reordered-keys false positives, and the pretty-print flag fixes the whitespace false positives. But the textual diff still treats arrays as line-of-text comparisons (so a head insertion still shows every later position as changed, exactly like the index comparison here), it cannot tell you "this changed type" from "this got removed and a new value appeared at the same place," and it does not give you machine-readable paths. This tool is built for the case where you want to know "exactly which paths changed" and want the answer in a format you can paste back into a Postman test, a `jq` selector, or a controller breakpoint without converting between path notations.

What happens when one side is invalid JSON?

The status bar tells you which side failed and why — `Original JSON is invalid — <parser message>` or `Changed JSON is invalid — <parser message>`, with the parser's own line/column information. The page does not try to "fix" malformed JSON or to do partial diffs across an invalid boundary; if you need to clean up the source first, paste it into the JSON formatter, fix the error there, then come back. The honest contract is that diff requires two valid JSON values — anything else is a parser problem, not a diff problem.

Is there a file size limit?

Each side stays under roughly 10 MB. The walker holds both parsed values in memory before emitting the change list, so a 50 MB session log on each side will stall on lower-RAM devices. For multi-megabyte payloads the right tool is usually a streaming JSON differ in Node (`@graphql-tools/json-canonicalize` plus a custom walker, or `dyff` for YAML/JSON workflows) or a domain-specific diff like `git diff` on canonicalised exports. This tool is built for the everyday "paste two API responses or two configs into a workspace" case.

Will this tool stay free?

The basic workflow is designed to stay free. Paid upgrades later will focus on bigger limits, batch work, OCR, saved presets, and ad-free use.