DiffViewer

<DiffViewer> compares two versions of a record and highlights added, removed, changed, and unchanged fields. Use it inside a <RecordTimeline> entry to show what changed in an audit-log update, or standalone to compare arbitrary objects.

Installation

pnpm dlx shadcn@latest add @shadmin/diff-viewer

Usage

import { DiffViewer } from "@/components/admin";
 
<DiffViewer
  before={{ name: "Notebook", price: 9.99, stock: 12 }}
  after={{ name: "Notebook Pro", price: 14.99, stock: 12 }}
  mode="side-by-side"
/>;

Modes

  • side-by-side (default) — two-column layout with before on the left, after on the right, connected by an arrow.
  • inline — single column. Removed values render with strikethrough, added values with underline, and changed values show before → after together.

Diff statuses

StatusWhenVisual
unchangedBoth values are strictly (or JSON) equalNeutral background
addedField missing in before, present in afterEmerald
removedField present in before, missing in afterRose
changedBoth present, values differAmber

Props

PropTypeDefault
beforeRecord<string, unknown>
afterRecord<string, unknown>
fieldsstring[]all keys (union of before + after)
labelsRecord<string, string>{}
formattersRecord<string, (v) => ReactNode>{}
mode"inline" | "side-by-side""side-by-side"

Custom formatters

Use formatters for non-string values:

<DiffViewer
  before={{ tags: ["a", "b"] }}
  after={{ tags: ["a", "c"] }}
  formatters={{ tags: (v) => (Array.isArray(v) ? v.join(", ") : "—") }}
/>

fields

By default, <DiffViewer> shows the union of all keys from before and after. Pass an explicit fields array to control order and subset:

<DiffViewer before={before} after={after} fields={["name", "price"]} />

labels

Override the display label for any field:

<DiffViewer
  before={before}
  after={after}
  labels={{ name: "Product Name", price: "Unit Price" }}
/>

i18n

KeyDefault
ra.diff_viewer.fieldField
ra.diff_viewer.beforeBefore
ra.diff_viewer.afterAfter