JSON Diff Viewer — Free Recursive Structural JSON Comparator

JSON Diff Viewer · Dashboards · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

True recursive diff
Walks real JS object/array structure, not text lines.
Correct nested handling
Changes at any depth report at their exact location.
Added/removed/changed tags
Every value gets one precise status, not a guess.
Old → new value display
Changed primitives show both sides explicitly.
Formatted, indented output
Bracket-correct pretty-printed JSON with inline markers.
Live debounced diffing
Recomputes 200ms after the last keystroke.
Friendly parse errors
Names which input is invalid JSON and why.
Zero dependencies
No diff library — genuine recursive comparison logic.

About this UI Snippet

JSON Diff Viewer — A Real Structural Diff, Not a Text Diff

Screenshot of the JSON Diff Viewer snippet rendered live

A text/line diff on two pretty-printed JSON blobs is famously misleading — reordering keys or reformatting whitespace produces a wall of red and green even when the data is identical. This tool instead parses both inputs and walks them recursively as JavaScript values, so the diff reflects actual structural differences.

The recursive comparison: diffValue()

diffValue(a, b) branches on the actual types of both values. If both are plain objects, it unions their key sets, sorts them for stable output, and recurses into diffValue for every key present in both — keys only in a are tagged removed, keys only in b are tagged added. If both are arrays, it walks index by index the same way, tagging extra trailing indices as added or removed. For anything else (primitives, or mismatched types like a number replaced by an object), it falls back to a value comparison via JSON.stringify equality and tags the result changed with both the old and new value preserved, or same if they're identical.

A diff tree, not a flat list

Every recursive call returns a node describing its own status — added, removed, changed, same, or nested (an object/array that contains changes somewhere inside it) — plus, for objects and arrays, a children/items structure holding the same kind of node for every key or index. renderDiff() then walks that tree and prints indented, properly bracketed JSON, coloring each line by its node's status: green + for added, red - for removed, yellow ~ with an old → new pair for changed, and neutral gray for unchanged context — so nested changes stay visually anchored inside their real object/array structure instead of flattening into an unreadable list.

Handles nesting correctly, at any depth

Because diffValue calls itself for every object/array value, a change three levels deep inside a nested settings object is caught and reported at exactly that depth, with every ancestor object correctly marked nested so it still renders (unchanged siblings included) rather than being collapsed away. Pair this with a code diff viewer for comparing raw text, or a JSON tree viewer for exploring a single document.

Build with AI

Build, Understand, Optimize, and Extend It With AI

Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to trace through diffValue() for a specific nested example — e.g. a key that's added at the top level and a value that changes three levels deep inside a nested object in the same comparison — and explain exactly how the returned diff tree represents both changes simultaneously. It's also useful for reasoning about edge cases — ask what happens when comparing an array to an object at the same key, or when one side has a key whose value is undefined versus the key being entirely absent, since JSON.stringify treats those differently. For extensions, ask it to add an "ignore key order" toggle for arrays (treating them as unordered sets for comparison), add a summary count of total additions/removals/changes at the top, or add a button to copy just the changed subset as a JSON patch. Treat the code less like a finished artifact and more like a starting point for a conversation.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a "JSON diff viewer" in plain HTML, CSS, and JavaScript — no diff library, implement the comparison logic yourself.

Requirements:
- Two textareas ("Before" and "After") pre-filled with sample JSON objects that have some overlapping keys, some added/removed keys, some changed values, and at least one nested object and one array, so the demo shows every diff type at once.
- CRITICAL: implement a genuine recursive structural diff function, not a text/line diff. Given two parsed JSON values, it should: for two plain objects, union their key sets and recursively diff every shared key, tagging keys present only in the first as "removed" and only in the second as "added"; for two arrays, walk index by index the same way; for anything else (primitives, or mismatched types like an object replaced by a number), compare by value equality and tag the result "changed" (keeping both the old and new value) or "same". Each recursive call should return a small descriptive node (its status, and for objects/arrays, the same kind of node for every child) so the overall result is a full diff tree mirroring the input's shape, correctly handling any nesting depth.
- Render the diff as formatted, properly indented JSON output (not a flat list) by walking that diff tree: added values shown in green with a "+" marker, removed values in red with a "-" marker, changed primitive values in yellow showing "oldValue -> newValue", and unchanged values in neutral gray as context — with objects and arrays rendering their real brackets/braces and nested children indented correctly, mirroring how the JSON would normally pretty-print.
- Recompute the diff live as either textarea is edited, debounced by roughly 200ms. Wrap each JSON.parse call in its own try/catch and show a clear error message naming which input (Before or After) failed to parse and why, without crashing the diff for valid input.
- If the two documents are structurally identical, show an explicit "no differences" message rather than an empty output area.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JSTwo sample JSON documents load and diff immediately.
  2. 2
    Edit the "Before" JSONThe diff recomputes live, debounced by 200ms.
  3. 3
    Edit the "After" JSONAdded, removed, and changed keys highlight instantly.
  4. 4
    Read the output+ green additions, - red removals, ~ yellow old → new changes.
  5. 5
    Try nested changesEdit a value inside settings to see depth-correct diffing.
  6. 6
    Break the JSON syntaxA parse error names exactly which side and why.

Real-world uses

Common Use Cases

API response debugging
Compare two payloads to spot an unexpected field change.
Config/feature-flag review
See exactly what changed between two config versions.
Code review tooling
Pair with a code diff viewer for JSON fixtures.
Data migration QA
Verify a transform only changed the fields it should.
Webhook/event payload comparison
Diff two event samples to spot schema drift.
Documentation tools
Pair with a JSON tree viewer for schema docs.

Got questions?

Frequently Asked Questions

A text diff compares two strings line by line, so reordering keys, changing indentation, or reformatting whitespace produces spurious differences even when the underlying data is identical. This tool instead parses both inputs with JSON.parse and recursively compares the resulting JavaScript objects and arrays by key and index, so only genuine structural differences — a key that's actually missing, added, or holds a different value — are reported.

diffValue(a, b) checks the types of both values. For two plain objects, it unions and sorts their key sets, then recursively calls diffValue on every shared key (tagging keys only in one side as added or removed). For two arrays, it walks index by index the same way. For anything else — primitives or mismatched types — it compares via JSON.stringify equality and returns either "same" or "changed" with both the old and new value attached. Every recursive call returns a small "node" describing its own status, which the renderer later walks to produce the formatted output.

Yes — because diffValue calls itself for every object or array value it encounters, a change buried inside a deeply nested object is detected and reported at its exact location, while every ancestor object along the way is marked "nested" (meaning it contains a change somewhere inside) so it still renders fully, including its unchanged sibling keys, rather than being flattened or collapsed.

diffValue only recurses when both sides are objects, or both sides are arrays. If the types don't match that way — for example a key that was an object in "Before" and a plain number in "After" — it falls through to the primitive comparison path, which correctly reports the whole value as "changed" with the old object and new primitive both shown, rather than trying to diff incompatible structures key by key.

The diffValue and renderDiff functions have no DOM dependency until the final render step, so you can keep them as-is and swap only the rendering: instead of building DOM line elements directly, walk the same returned diff tree recursively in your framework's template syntax, mapping each node's status to a CSS class the same way. Keep the JSON.parse calls in a try/catch tied to your input state so parse errors surface the same way.