webdevpuneet.com

Row-Level Diff Table — Compare Two Datasets HTML CSS JS

Row-Level Diff Table · Tables · Plain HTML, CSS & JS · Live preview

CategoryTables

Copy & paste

Source Code

<div class="dft-card">
  <div class="dft-head">
    <h3>Config diff — v1.4 vs v1.5</h3>
    <div class="dft-legend">
      <span><i class="dft-sw dft-added"></i>Added</span>
      <span><i class="dft-sw dft-removed"></i>Removed</span>
      <span><i class="dft-sw dft-changed"></i>Changed</span>
    </div>
  </div>
  <table class="dft-table">
    <thead><tr><th>Key</th><th>Value</th><th>Owner</th><th>Env</th></tr></thead>
    <tbody id="dftBody"></tbody>
  </table>
</div>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

Real keyed row comparison
Rows are matched by a unique identity field via lookup maps, not by array position.
Live-computed added/removed/changed
Every row's status is determined by set membership across both datasets, not hardcoded.
Field-level change detection
Object.keys comparison finds exactly which fields differ per changed row.
Cell-level highlighting
Only the specific differing cells are called out, showing old value struck through above new.
Git-diff visual language
Green/red/yellow with left-border accents mirrors familiar code-review conventions.
Union-of-keys iteration
Every key present in either dataset is included, so nothing added or removed is silently dropped.
Status badges
Small Added/Removed/Changed badges next to the key reinforce the row-level color coding.
Fully data-driven
No precomputed highlight state — editing the source arrays alone updates the rendered diff.

About this UI Snippet

Row-Level Diff Table — Real Computed Added / Removed / Changed Highlighting

Screenshot of the Row-Level Diff Table snippet rendered live

Comparing two versions of a dataset — a config before and after a deploy, a spreadsheet import against the current database, an API response across two environments — is much easier to audit as a single table than as two separate ones. This snippet computes a genuine row-by-row and field-by-field diff between two arrays in plain JavaScript and renders it as one table with added rows in green, removed rows struck through in red, and changed rows in yellow with only the specific cells that actually differ called out.

Keyed comparison, not positional

The diff function builds a lookup map from each dataset keyed by a unique field (key in the demo, but any unique id works), then walks the union of keys present in either version. This is deliberate: comparing row 3 of one array against row 3 of another only works if nothing was reordered, inserted, or removed above it, which is rarely true in practice. Keying by identity means a row that moved position between the two datasets is still correctly matched and compared, not falsely flagged as removed-then-added.

Three real outcomes per key

For every key, the diff falls into exactly one of three buckets computed live: present only in before (removed), present only in after (added), or present in both. For the "present in both" case, Object.keys(a).filter(...) compares every field's value between the two rows and collects which specific fields actually differ — a key with all identical fields is marked unchanged and rendered like a normal row, no highlighting applied.

Cell-level highlighting, not just row-level

A changed row doesn't turn every cell yellow — only the cells whose value actually differs get the dft-cell-changed treatment, showing the old value struck through in small text above the new value in full color. This is what makes the diff genuinely useful: with a row-only highlight you'd know *something* changed but have to hunt for what; the cell-level detail answers that directly, sourced from the changedFields array the comparison function actually computed, not hardcoded per row.

Visual language borrowed from code diffs

The color scheme and left-border accent mirror the added/removed/changed convention from git diffs and code review tools — green for added, red-with-strikethrough for removed, yellow for modified — because that visual vocabulary is already familiar to anyone who reviews changes for a living, making the table legible at a glance without reading the legend.

Fully data-driven, add a row and it just works

Nothing about which rows are added, removed, or changed is precomputed or hand-annotated in the arrays — BEFORE and AFTER are just plain data, and every highlight in the rendered table is the live output of the diff() function running against whatever is currently in those two arrays. Change a value, add a key, remove one, and the table's highlighting updates correctly with no manual bookkeeping.

Customizing it

Swap the identity field, diff nested objects field-by-field recursively, or add a filter to show only changed rows. Pair it with an editable table for the "before" state or a sortable table to reorder the diff results.

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JSA config diff renders comparing two sample datasets, BEFORE and AFTER.
  2. 2
    Read the legendGreen marks added rows, red-strikethrough marks removed rows, yellow marks changed rows.
  3. 3
    Inspect a changed rowOnly the specific cells that differ are highlighted, showing the old value struck through above the new one.
  4. 4
    Edit BEFORE or AFTERAdd, remove, or change a value in either array — the diff recomputes and the highlighting follows automatically.
  5. 5
    Change the identity keySwap 'key' for any unique field in your data — the comparison is keyed, not positional.
  6. 6
    Add more fieldsAdd properties to the row objects; changedFields is computed generically over Object.keys, so new fields diff automatically.

Real-world uses

Common Use Cases

Config and feature-flag diffs
Compare configuration or flag state across deploys, environments, or points in time.
Database migration review
Show what an import or migration will add, remove, or change before committing it.
API response comparison
Diff a response across two environments or API versions field by field.
Approval and audit workflows
Pair with an editable table so reviewers can see proposed edits before approving.
Spreadsheet import review
Compare an uploaded CSV against existing records before applying a bulk update.
Version comparison tools
A row-level counterpart to a comparison table for comparing two states of the same entity over time.
Related: Expandable Row Detail Table
See the Expandable Row Detail Table for a related tables pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

The diff function marks it status: 'unchanged' since changedFields comes back empty, and the row renders with no highlight class and no badge — it looks like a completely normal row, which is the correct behavior since nothing about it actually changed.

Build a composite key by concatenating multiple fields (e.g. row.category + '|' + row.name) when constructing beforeMap and afterMap, and use that same composite when building the keys union array — the rest of the diff logic is unaffected since it only cares that the key uniquely identifies a row.

Replace the simple a[field] !== b[field] comparison with a deep-equality check (a small recursive function, or JSON.stringify(a[field]) !== JSON.stringify(b[field]) for simple cases) so a field whose value is itself an object or array is correctly detected as changed only when its contents actually differ.

Filter the results array from diff() with results.filter(function(e){ return e.status !== 'unchanged'; }) before mapping it to row HTML — the diff computation itself doesn't need to change, only what you choose to render from its output.

Keep the diff(before, after) function exactly as-is — it's pure JavaScript with no DOM dependency, so wrap it in a useMemo (React), computed (Vue), or getter (Angular) keyed on your before/after data, and map the returned array to row components applying the same status-based classes.