Two JSON documents can be byte-for-byte different and functionally identical — or look nearly identical and be structurally broken. Closing that gap is the entire point of a JSON diff, and it's a genuinely different problem than diffing two text files. If your two objects are short and you just want to paste two JSON objects into a browser tool and see the result, that guide is the click-by-click walkthrough. This one covers what's actually happening underneath: why a plain text diff misreads JSON, what a structural diff computes instead, the two IETF standards for representing a difference JSON that diff tools can store or apply as data, and where our own browser tool's honest limits sit. If your document is XML rather than JSON, the diffing problem looks different again — see the XML compare guide for namespace and attribute-order handling instead.

The Short Answer: What a JSON Diff Actually Compares

A JSON diff compares two JSON documents by structure and value — which keys were added, removed, or changed, and at what path inside the tree — instead of by which lines of text moved. A correct json comparison is invariant to object key order, invariant to whitespace and indentation, and, once numbers are normalized, invariant to equivalent numeric formats. It is not invariant to array order, because a JSON array is an ordered sequence and a JSON object is an unordered collection of key-value pairs — that's the definition in RFC 8259, not a convention any particular tool chose. Nearly every surprising result you'll get from a diff json tool traces back to that one asymmetry: objects don't care about order, arrays do.

That distinction is also why running two JSON files through a generic text differ — the same diff command you'd use on two plain .txt files — routinely produces a wall of red and green for documents that are, semantically, identical. The next few sections cover exactly why that happens and how a real online json compare tool avoids it.

Why a Plain Text Diff Lies About JSON

Feed two structurally identical JSON objects into a plain text differ and it will still report a change, because a text diff only knows about lines — it has no idea what a JSON object or array even is.

{ "a": 1, "b": 2 }
{ "b": 2, "a": 1 }

$ diff a.json b.json
1c1
< { "a": 1, "b": 2 }
---
> { "b": 2, "a": 1 }

Two keys, same values, same document — and a naive attempt to compare two JSON files this way reports the whole line as replaced, because the key order changed and a text diff can only compare position, not meaning.

Numbers compound the problem. 1.0e6 and 1000000 are the same number written two different ways — scientific notation versus a plain integer — and a text diff sees two unrelated strings. 1.0 and 1 are the same kind of mismatch one order of magnitude down: same value, different token. Neither pair equals the other pair — a million is not one — but within each pair, a text diff flags a change that a JSON-aware comparison should not.

Formatting adds pure noise on top of that: pretty-printed JSON versus minified JSON, 2-space indentation versus 4-space, a trailing newline at end of file versus none, CRLF line endings versus LF from a file that crossed from Windows to Linux. None of it changes a single value in the document, and all of it shows up as a diff in a tool that only understands text.

Same keys, different order — two ways to read it Text diff (line-based): sees two different lines { "a": 1, "b": 2 } − a.json, line 1 { "b": 2, "a": 1 } + b.json, line 1 Structural diff (path-based): matches each key by path a: 1 b: 2 b: 2 a: 1 Matched by path → 0 changes, order ignored
A text diff compares position and flags the whole line as replaced; a structural diff matches "a" to "a" and "b" to "b" by key path and reports zero changes — the key-order false positive from the code above, seen visually.

Two more structural quirks round out the list, and they get their own callouts because normalization alone — covered a few sections down — doesn't fix either one:

  • Array insertion at index 0 shifts every later index. Insert a new first element into an array and a positional diff — one that compares index 0 to index 0, index 1 to index 1, and so on — reports every element as changed, because everything downstream of the insertion point moved one slot to the right.
  • The escape-sequence trap. "é" written as a single precomposed Unicode codepoint and "é" written as the letter e followed by a combining acute accent render identically to a human reader, but they are different byte sequences and different JSON string values. Neither JSON parsing nor most diff normalization applies Unicode normalization (NFC/NFD) by default, so this one survives even a careful structural diff unless something explicitly normalizes it.

Structural vs Textual Comparison: The Real Distinction

Every JSON diff approach falls into one of two families, and knowing which one a tool uses tells you what it will and won't get wrong.

Textual diffing treats the input as lines of characters — usually the Longest Common Subsequence (LCS) algorithm behind the classic Unix diff — and reports insertions and deletions of lines. It's fast, language-agnostic, and works on anything, which is exactly why it also can't tell a cosmetic reformat from a real data change. Every failure mode in the section above is a textual-diff failure mode.

Structural diffing parses both documents into a tree first, then walks the tree comparing nodes by key path rather than by line position — user.address.city in document A is compared against the same path in document B regardless of where either line landed after formatting. Object key order stops mattering because the comparison isn't positional anymore; it's addressed by path. This is what every serious json difference checker does under the hood, whether it's a CLI tool, a JavaScript library, or a browser extension.

The practical shortcut that makes textual diffing usable on JSON anyway — instead of building a full structural diff engine — is normalization, covered next. That's the real distinction under any tool you use to compare 2 JSONs, from a five-line script to a full IDE plugin.

Normalize First: The Trick That Makes Text Diff Work on JSON

You don't need a structural diff engine to get a structurally correct result — you need to normalize both documents into one canonical text form first, then hand that to an ordinary text differ. Normalization means: parse the JSON, recursively sort every object's keys into a fixed order, and re-serialize with consistent whitespace. Two documents that are structurally identical but were serialized differently become byte-identical after normalization, and two documents with a real difference still show exactly that difference — nothing more, nothing less.

This is precisely what the Normalize button in the Diff Checker browser extension does for JSON: it parses the input, sorts object keys alphabetically (case-insensitive), and re-serializes with 2-space indentation. Arrays keep their original order by design — sorting array elements would silently change what the array means, since array order carries information that object key order doesn't. If the text isn't valid JSON, Normalize falls back to plain whitespace normalization instead of failing outright. Run Normalize on both sides before you diff and the key-order false positive from the section above disappears entirely — that single step is the most important habit in any serious json compare tool workflow, browser-based or not. The same button does format-appropriate work on non-JSON input too — sorting CSS properties, collapsing whitespace — which the guide to finding the difference between two strings walks through on plain text and code.

One honest caveat: because Normalize round-trips the document through parse and re-serialize, it also collapses 1.0e6 and 1000000 down to the same text — usually exactly what you want. But that round-trip passes through a JavaScript number, and JavaScript numbers only represent integers exactly up to 253−1 (9,007,199,254,740,991). A large database ID or snowflake ID above that threshold can lose precision in the round-trip. If your JSON carries big numeric IDs, treat a post-normalize numeric diff on those specific fields with a healthy amount of suspicion and check the raw values directly.

Where Normalization Still Isn't Enough

Key sorting and re-serialization solve the most common false positives, but three cases survive it.

Arrays don't get reordered — by design — so array insertions still cascade. Normalize sorting keys never touches array element order, because it can't: an array's order is data, not metadata. Insert a new element at the front of an array and every element after it is still, correctly, one index further along than it was before:

// before
["alpha", "beta", "gamma"]

// after inserting "new" at index 0
["new", "alpha", "beta", "gamma"]
Insert one element — every index after it shifts before 0 alpha 1 beta 2 gamma after 0 new 1 alpha 2 beta 3 gamma replaced replaced replaced added Naive positional diff: 3 replacements + 1 addition, for a single insert
A single insertion at index 0 pushes alpha, beta, and gamma one slot right; a positional diff compares old index to new index and reports three replacements instead of one insert.

A naive positional comparison — index 0 vs index 0, index 1 vs index 1 — reports "alpha" replaced by "new," "beta" replaced by "alpha," "gamma" replaced by "beta," plus one addition, when what actually happened is a single insertion. This isn't a bug in normalization; it's the correct, literal reading of an ordered sequence. Fixing it requires a smarter array-matching strategy, covered a few sections down.

Numbers beyond safe-integer range, as covered above — round-tripping through a parser can silently change a huge ID's precision by a few digits, and a diff on the normalized output won't tell you the original raw values differed.

Unicode normalization forms aren't touched by JSON parsing or re-serialization at all. JSON.parse and its equivalents preserve string content exactly; they don't apply NFC or NFD Unicode normalization. Two strings that display identically but use different Unicode representations of the same accented character stay different after any amount of key-sorting and re-indentation, because the difference lives inside a string value, not in the document's structure.

JSON Patch (RFC 6902): The Standard Diff Output

RFC 6902, JSON Patch, standardizes what a JSON diff looks like as data instead of as colored lines on a screen — a JSON document you can store, transmit, or apply. Its media type is application/json-patch+json, and a patch is a JSON array of operation objects. Six operations are defined: add, remove, replace, move, copy, and test.

[
  { "op": "replace", "path": "/user/name", "value": "Alice" },
  { "op": "add", "path": "/user/tags/-", "value": "verified" },
  { "op": "remove", "path": "/user/legacyId" }
]

Each operation's path is a JSON Pointer (RFC 6901) — a slash-delimited path into the document, like /users/0/name for the name key of the first element in a users array. A trailing - in an array path, as in /user/tags/- above, means "append after the last element." Two characters need escaping inside a pointer segment because they're also the pointer's own syntax: ~1 stands for a literal / in a key name, and ~0 stands for a literal ~.

Operations apply sequentially, in array order, and the whole patch aborts if any single operation fails — there's no partial-apply mode. That makes JSON Patch precise and auditable: you can see exactly which operation, at exactly which path, produced exactly which change, and a test operation can even assert a value before proceeding, so the patch fails loudly instead of applying against a document it no longer expects.

JSON Merge Patch (RFC 7386) and Its null Problem

RFC 7386, JSON Merge Patch, takes a different, much simpler approach: media type application/merge-patch+json, and the patch document mirrors the shape of the thing it's patching, rather than listing operations.

{
  "name": "Alice",
  "email": null,
  "address": { "city": "Berlin" }
}

Applying that patch sets name to "Alice", merges city into the existing address object, and — this is the part worth knowing cold — deletes the email key, because null in a merge patch means "remove this." That convention is also the format's one real limitation: since null is reserved to mean delete, you can never use a merge patch to set a field's actual value to null — there's no escape hatch for it in the spec. The second limitation is arrays: a merge patch cannot patch inside an array at all — it can only replace an array wholesale, value for value, because merge patch has no path syntax to address "index 2 of this array" the way JSON Pointer does.

null in a merge patch means delete the key Original document name: "Bob" email: "bob@x.com" city: "NYC" apply Merge Patch name: "Alice" email: null city: "Berlin" Result document name: "Alice" email: (deleted) city: "Berlin" null always means delete — a merge patch can never set a field to null on purpose
The patch's null on email doesn't set email to null — it deletes the key entirely. Only name and city actually change; email disappears from the result document.

RFC 6902 vs RFC 7386: Which One to Use

Both are legitimate, standardized ways to represent a difference JSON that diff tools can hand off as data — they trade precision for simplicity in opposite directions.

Property JSON Patch (RFC 6902) JSON Merge Patch (RFC 7386)
Media type application/json-patch+json application/merge-patch+json
Shape Array of operation objects (add/remove/replace/move/copy/test) Mirrors the target document's shape
Can set a value to null Yes — replace with "value": null No — null always means delete
Array edits Addresses individual indices via JSON Pointer Replaces the whole array, no partial edits
Readable by a human at a glance Moderate — verbose but explicit High — looks like the document itself
Best fit API diffs, audit trails, precise programmatic patching Config overlays, PATCH endpoints on REST APIs

Reach for JSON Patch when you need an exact, ordered, auditable record of what changed — the kind of thing you'd log or replay. Reach for JSON Merge Patch when the patch itself should be easy for a person to write by hand, such as a small config override file, and you can live with its two constraints: no explicit nulls, no partial array edits.

Comparing JSON on the Command Line (jq, jd, dyff)

For a linux json diff or any terminal-first workflow, three tools cover almost every case.

jq: normalize, then diff

The canonical way to compare two json files from a shell is to normalize both through jq and hand the result to diff:

diff <(jq -S . a.json) <(jq -S . b.json)

jq's -S flag sorts object keys recursively before printing — the exact CLI equivalent of the Normalize button covered above. Its limitation is the same one normalization always has: jq -S has no smart array matching, so array comparisons stay purely positional. It's the right first tool to reach for, and the wrong one if your JSON has arrays that get reordered or gain elements mid-sequence.

jd: a diff tool built specifically for JSON and YAML

jd is a CLI (and library) that diffs and patches JSON and YAML directly, with no jq pipeline required. It's actively maintained, prints its own diff format by default, and can also emit output as an RFC 6902 JSON Patch or an RFC 7386 JSON Merge Patch — useful when you want a diff json result you can immediately apply somewhere else instead of just reading. jd also ships a web UI for the same comparisons when you'd rather not leave the browser.

dyff: path-based diffs for config and Kubernetes

dyff diffs YAML and JSON and prints results as human-readable, path-based statements (Spruce dot-syntax, like spec.replicas) instead of a raw structural dump — built specifically for diffing Kubernetes manifests and Helm chart output, where a plain text diff on rendered YAML is close to unreadable. It's installable via Homebrew.

All three sit next to the broader Linux/Unix diffing toolchain — if diff's own flags and output format aren't second nature yet, the diff command guide and the unified diff format guide cover the syntax these JSON-specific tools build on top of. For a broader survey of GUI and CLI options beyond these three, see the Linux diff tool roundup. Whether you compare JSON files locally with a one-off jq pipeline or compare 2 JSON files automatically inside a CI job with jd, normalizing before diffing is the same rule either way.

Three CLI approaches to a linux json diff jq -S + diff diff <(jq -S a) <(jq -S b) Sorts keys before diffing No smart array matching Best for Quick one-off diffs jd jd a.json b.json Native JSON and YAML diff Emits RFC 6902 / 7386 Best for A patch you can apply dyff dyff between a.yml b.yml Path-based, human output Built for k8s / Helm charts Best for Config and manifest diffs
jq -S normalizes but stays positional on arrays; jd diffs JSON and YAML natively and can emit a patch; dyff trades raw structure for human-readable, path-based output built for Kubernetes manifests.

JavaScript Libraries for Programmatic JSON Comparison

When the comparison needs to live inside test code, a CI check, or an application rather than a terminal, a library beats a CLI call — you compare JSON objects already in memory and assert on the result, instead of shelling out and parsing text output.

  • jsondiffpatch — a deep-diff library with smart array matching (LCS-based, so it detects moves and insertions instead of treating every shifted index as a replacement), plus a text-diff mode for long string values. It can export its result as an RFC 6902 patch. This is the closest thing to a full structural differ in the JS ecosystem, and the one to reach for when array reordering is the specific problem you're fighting.
  • microdiff — deliberately the opposite trade-off: under 1KB, zero dependencies, fast, and simple. It doesn't offer jsondiffpatch's array-matching options — a reasonable choice when you just need "did anything change and where" without the overhead.
  • deep-diff — a widely used deep-equality/diff library that reports what changed (new, deleted, edited, array-changed entries) without producing an applicable patch. Good for logging or assertions, not for generating something you'd send over the wire.
  • json-diff (the andreyvit package on npm) — still widely depended on across the ecosystem, but its last publish was roughly three years ago. Treat it as maintenance-mode rather than actively developed, not as abandoned — plenty of production code still runs on it.

For a side-by-side look at plain equality checks rather than diffing — JSON.stringify comparison, Lodash isEqual, fast-deep-equal, and where each one breaks — see the dedicated JavaScript object comparison guide. And if the JSON you're comparing is really an array or a list of records rather than a single object, the guide to JSON lists and arrays covers parsing and diffing that shape specifically. On the Python side, deepdiff with ignore_order=True covers the same ground the JS libraries above do — see comparing two files in Python for the full method rundown, filecmp and hashlib included.

Which approach to reach for

Five approaches, one table: a quick reference for which json diff method actually fits the job in front of you, before the next section goes deep on the browser tool specifically.

Approach Structural? Array matching RFC 6902 output Best for
jq -S + diff No — normalize, then line diff Positional only No Quick one-off CLI checks
jd Yes Positional Yes (or RFC 7386) Scripted diffs that need a patch
dyff Yes Positional No Kubernetes and Helm manifests
jsondiffpatch Yes LCS-based — detects moves Yes (exportable) JS apps fighting array reordering
Diff Checker (browser) No — normalize, then text diff Positional No Fast, private, no-install compares

Comparing Two JSON Files in the Browser

The Diff Checker Chrome extension is a fast, private way to diff JSON online — compare two JSON files right in your browser, with no CLI, no library, and no account required. Everything below is what it actually does today, verified against the current build.

  • Format — Prettier-powered pretty-printing. JSON is one of the supported parsers, alongside JS, TS, HTML, CSS, YAML, Markdown, and GraphQL. It's a manual click, not an automatic on-paste behavior.
  • Normalize — for JSON specifically, parses, sorts object keys alphabetically (case-insensitive), and re-serializes with 2-space indentation, exactly as described above; falls back to whitespace normalization on invalid JSON.
  • Compare method — a dropdown with exactly three options: Smart Diff (the default), Ignore Whitespace, and Classic (LCS).
  • Split or Unified view — side-by-side is the default; both panes are directly editable.
  • Show Diff Only — a toggle with a context-lines selector (0, 1, 2, 3, or 5 lines) that collapses unchanged regions so a long file doesn't bury the two or three sections that matter.
  • Wrap, Swap, and Copy Diff — line wrapping, one-click pane swap, and copying the diff to your clipboard as plain text with +/- prefixes.
  • History — past comparisons are stored locally in IndexedDB, nothing server-side.
  • Open Files — accepts .json plus .txt .js .jsx .ts .tsx .html .css .md .py .java .c .cpp .cs .go .rb .php .sql .yaml .yml and DOCX/XLSX, up to two files at a time, 50 MB hard cap per file.
  • Syntax highlighting via Monaco Editor, with JSON among 17 bundled languages.
  • KeyboardAlt+ jumps to the next change, Alt+ to the previous one. These are the only two custom shortcuts the extension defines.
  • AI Summary — optional and off by default. Bring your own OpenAI API key, stored locally, and it produces a plain-English summary plus a key-changes list. Turning it on means the compared content is sent to OpenAI — worth knowing before enabling it on anything sensitive.
  • Compare Tabs Source — an extension-only feature that pulls the HTML source of open browser tabs directly into the comparison, no copy-paste.

Everything above the AI Summary bullet runs entirely client-side — no network call happens anywhere in the diff path, nothing you paste or open is uploaded. The one thing that does contact a server is the opt-in AI Summary, and only after you've supplied your own key. There's also a browser-only version of the same tool at diffchecker.pro/compare if you'd rather not install an extension at all.

Extension toolbar: Normalize, Compare method, Show Diff Only Normalize Compare Smart Diff ▾ Show Diff Only (0–5) Sorts object keys case-insensitively Smart Diff, Ignore Whitespace, or Classic Hides unchanged lines, 0–5 lines of context Alt+↓ next change · Alt+↑ previous change
Normalize, Compare method, and Show Diff Only are the extension's core JSON controls; Alt+↓ and Alt+↑ are the only two custom keyboard shortcuts it defines.

Here's the honest boundary, stated plainly rather than buried in fine print: this is a fast, normalize-then-text-diff tool, not a full structural JSON differ. Format plus Normalize plus Smart Diff handles the overwhelming majority of real-world JSON comparison — pasting two API responses, checking a config change, eyeballing what a migration script produced — and it does it instantly, privately, with no upload. What it does not do: true array-order-insensitive comparison (arrays stay positional by design, as covered above), JSON-schema validation or awareness, RFC 6902/7386 patch output, a three-way merge, applying a patch back onto a document, or exporting the diff to a file. If you specifically need machine-readable patch output or genuine array-identity matching — not just position — that's exactly the gap jd or jsondiffpatch from the sections above are built to fill. Many other online json comparator options draw this line differently: Diffchecker.com, for instance, gives you a free web tier for basic compares but reserves desktop apps and permanent sharing links for its Pro + Desktop tier at $15/month (Legal at $20/month, Enterprise at $40/month, as of 2026). Diff Checker's browser extension has no paid tier at all — what's described in this section is the entire feature set, free, with no account.

Array Matching: Why "Smart" Diffs Disagree

Ask three different JSON diff tools to compare the same two arrays and you can genuinely get three different answers, and it isn't a bug in any of them — it's because array diffing has no single correct algorithm, only strategies with different trade-offs.

Positional matching compares index 0 to index 0, index 1 to index 1, and so on. It's what jq -S piped into diff does, and it's what our extension's Normalize step preserves by leaving array order untouched. It's simple and predictable, and it's the strategy that makes a single insertion at the front of an array look like every element changed, as shown earlier.

LCS-based matching — the approach jsondiffpatch takes — treats the array like two sequences and finds the longest common subsequence between them, the same core idea behind text-diff algorithms applied to array elements instead of lines. It correctly identifies "one element was inserted" instead of "everything shifted," as long as the elements themselves are directly comparable.

Same array change, three different verdicts Positional Compares index 0 to 0, index 1 to 1, and so on 3 replaced + 1 added LCS-based Finds longest common subsequence, like text diff 1 insert, 0 false diffs Identity-key Matches by a stable field like id, not by position Detects moves, needs id
Same insertion, three answers: positional counts three replacements, LCS-based correctly finds one insert, identity-key catches moves but only if you tell it which field is the id.

A third strategy, common in tools built for arrays of objects specifically, is identity-key matching: match array elements by a stable field like id rather than by position or content similarity, so an object that moved from index 3 to index 0 is recognized as "moved," not "removed and a different one added." None of the CLI or library tools covered in this guide default to that strategy automatically — it typically requires telling the tool which field is the identity key, because nothing in JSON itself marks one.

The takeaway: when two json compare tool outputs disagree on the same array change, check which matching strategy each one uses before assuming either is wrong. They're usually both computing a correct answer to two different questions.

Where JSON Diffs Show Up in Real Work

The theory above earns its keep in a handful of recurring situations.

API contract testing. Comparing a v1 and v2 response body for the same endpoint is how you catch a renamed or missing field before a client breaks on it in production — exactly the case a JSON object comparison is built to catch, and one where key-order noise from a re-ordered serializer would otherwise bury the one field that actually changed.

Config drift. tsconfig.json, package.json, CloudFormation templates, Kubernetes manifests rendered as JSON — diffing a live config against its source of truth surfaces exactly what an environment override or a manual edit changed. This is dyff's specific reason for existing, and it's a case where diffing two file versions in git gets noisy fast if the JSON isn't normalized first — a re-formatted file with zero real changes still shows as a full rewrite in git diff unless you normalize before comparing.

Code review. A pull request that touches a JSON config or fixture file is one of the more error-prone diffs to review by eye, because a reformat and a real change look identical at a glance in a raw text diff. Running the before/after through Normalize first turns that review from "scan 200 lines for the two that matter" into "read the two lines that changed."

Migrations. Moving records between systems — a SQL export reshaped into MongoDB documents, a v1 API schema migrated to v2 — means verifying every field transformed correctly, field by field, often across thousands of records where eyeballing isn't an option and a scripted diff (jq in a loop, deepdiff in a Python validation script, jsondiffpatch in a Node test) is the only realistic approach. When the migration spans formats — JSON on one side, CSV or SQL rows on the other — the broader playbook on comparing structured data sets covers when to flatten to rows before diffing. Comparing individual values correctly — numbers, strings, nulls — matters even more at that scale, since a single silent type coercion during migration can look identical to the human eye and still be structurally wrong.

Frequently Asked Questions

How do I compare two JSON files?

Three routes cover almost every case. In the browser, open or paste both documents into a diff tool, hit Normalize so object keys sort into the same order on both sides, then Compare — that's the fastest way to diff JSON online, with nothing to install and nothing uploaded. On the command line, piping jq -S output for both files into diff runs the same normalize-then-compare in one line, and jd a.json b.json can compare 2 JSON files natively without the jq pipeline. In application code, jsondiffpatch or microdiff returns the result as data you can assert on inside a test. Whichever route you take, normalize first: skipping that step is what makes a text differ report a wall of red and green instead of the difference JSON documents actually have.

What's the real difference between comparing JSON and comparing plain text?

A text diff compares two files line by line and knows nothing about JSON's structure, so it reports a change whenever a line moves — even when the underlying data is identical. A JSON diff compares by structure and value instead: which keys were added, removed, or changed, and at what path. That makes it invariant to object key order and formatting, but not to array order, because JSON objects are unordered collections of key-value pairs while JSON arrays are ordered sequences — that asymmetry is why arrays and objects behave differently under every JSON diff tool. In practice the two approaches can disagree completely about the same pair of files, and only one of them is answering the question you actually meant to ask.

Why does {"a":1,"b":2} show as different from {"b":2,"a":1} in a text diff?

Because a plain text diff compares lines, not meaning, and reordering the keys changes the line's text even though it doesn't change what the object represents. JSON objects are unordered by definition, so both objects are the same document. The fix is normalization: parse both documents, sort every object's keys into a fixed order, and re-serialize before diffing. Once both sides use the same key order, a plain text diff on the normalized output reports zero changes for these two objects, exactly as it should.

What is JSON Patch (RFC 6902) and when should I use it instead of JSON Merge Patch?

JSON Patch (RFC 6902) represents a diff as an ordered array of operations — add, remove, replace, move, copy, and test — addressed by JSON Pointer paths like /users/0/name, with media type application/json-patch+json. It applies sequentially and aborts entirely if any operation fails, which makes it precise and auditable. JSON Merge Patch (RFC 7386) is simpler: the patch mirrors the target document's shape and null means delete a key, but that convention means you can never set a value to null on purpose, and it can only replace arrays wholesale rather than editing inside them. Use JSON Patch when you need an exact, ordered record of what changed; use Merge Patch when the patch should be small and easy for a person to write by hand, and you can live with its two constraints.

How do I diff two JSON files on Linux from the command line?

The standard Linux JSON diff recipe combines jq with diff: diff <(jq -S . a.json) <(jq -S . b.json). The -S flag recursively sorts object keys before printing, which eliminates key-order false positives before diff ever runs — jq's limitation is that it has no smart array matching, so array comparisons stay purely positional. For JSON- and YAML-native diffing without a jq pipeline, jd is a maintained CLI and library that can also emit RFC 6902 or RFC 7386 output. For config and Kubernetes-style JSON/YAML, dyff prints human-readable, path-based results instead of a raw structural dump, and installs via Homebrew.

Why do different JSON diff tools disagree about the same array change?

Because array diffing has no single correct algorithm — only strategies with different trade-offs, and tools default to different ones. Positional matching compares index 0 to index 0 and so on; it's simple but makes a single insertion at the front of an array look like every element changed. LCS-based matching, the approach jsondiffpatch takes, finds the longest common subsequence between the two arrays and correctly spots insertions instead of a full shift. Identity-key matching compares array elements by a stable field like id rather than position, catching moves that the other two strategies would read as delete-plus-add. None of the tools covered in this guide default to identity-key matching automatically — it has to be configured, because nothing in JSON itself marks a field as an identity key.

What is the best JSON diff tool?

There isn't a single winner — there's a best fit per job, and the split is clean. For a quick visual check with nothing to install, a browser-based JSON difference checker wins: paste both sides, normalize, read the highlighted result. When you compare JSON files inside a script or a CI job, jq -S piped into diff covers the simple cases and jd covers the ones that need RFC 6902 or RFC 7386 patch output. For application code, jsondiffpatch handles the array reordering that positional tools get wrong, while microdiff wins on bundle size when you only need to know that something changed. Before you trust any online JSON comparator, check two things: whether it sorts object keys for you, and whether it compares your files entirely inside your own browser or ships them to a server first. That second question matters just as much whether you compare 2 JSONs by hand once or two hundred in a nightly pipeline.

Does comparing JSON in the Diff Checker browser extension upload my data anywhere?

No — the diff itself runs entirely client-side, inside your browser tab, with no network call anywhere in the comparison path. Format, Normalize, Compare, and History all work offline against local IndexedDB storage on your machine. The one feature that does contact a server is the optional AI Summary, which is off by default and requires you to supply your own OpenAI API key before it sends the compared content to OpenAI for a plain-English summary. Every other feature — including comparing two JSON files up to 50 MB each — never leaves your device.