Skip to content
WANCRAFT

C.193Typography & Text

Text Diff

An animated word- or character-level diff: deletions strike through, insertions type in, inline or side by side with a +12 −5 summary.

NewReactMotionTailwindadvanced

Overview

Text Diff compares two strings with Myers' diff algorithm, computed inline with no dependencies, at word level (punctuation and spaces are their own tokens) or character level. Results are tidied for reading: whitespace that only separates two changes joins them, and each change block lists what was removed before what was added. Insertions are underlined on a green tint and deletions struck through on a red one, so colour is never the only cue, and screen readers hear "[inserted: …]" and "[deleted: …]" around each change. Show it inline or as Before and After panes that stack in narrow containers. When the strings change, the transition plays: deletions strike through first, then insertions type in word by word. With Show changes off, deletions collapse out of the line and you are left with the clean new text. A header shows a "+12 −5" summary with a switch and a replay button, and the summary is announced politely after each new diff.

01Installation

Method 01 · Registry

CLI verified

Uses the shadcn CLI in a project initialised with shadcn init. Installs the file to components/ui/text-diff.tsx and adds its dependencies.

npx shadcn@latest add https://wancraft.my/r/text-diff.json

Method 02 · Copy and paste

  1. 1. Install the dependencies below.
  2. 2. Copy the source from the Code tab into components/ui/text-diff.tsx.
  3. 3. Import it and adjust props.
npm install motion
Dependencies
motion
Peer
react ≥ 18, tailwindcss ≥ 4
Added
13 Oct 2026
Updated
—

02Usage

The default configuration. Change properties above to generate a tailored example in the Usage tab.

example.tsx
import { TextDiff } from "@/components/ui/text-diff";

export function TextDiffExample() {
  return (
    <TextDiff
      from="Each one is built with care."
      to="Every part is made by hand, with care."
    />
  );
}

03Props

PropTypeDefaultDescription
fromstring—Required. The old text.
tostring—Required. The new text. Changing from / to plays the transition.
granularity"word" | "char""word"Diff whole words (spaces and punctuation as their own tokens) or characters.
mode"inline" | "split""inline"One merged paragraph, or Before / After panes (stacked below 32rem).
showChangesbooleantrueMark insertions and deletions; off collapses deletions. Also toggled by the header switch.
animatebooleantrueAnimate new diffs (deletions first, then insertions type in).
durationnumber900Length of the whole transition in ms.
toolbarbooleantrueHeader with the summary, Show changes switch and replay button.
insertColorstring"#22A06B"Underline, tint and summary colour of insertions.
deleteColorstring"#E5484D"Strike, tint and summary colour of deletions.
beforeLabel / afterLabelstring"Before" / "After"Pane titles in side-by-side layout.

04Features

  • 01Myers O((N+M)·D) diff, implemented inline, at word or character level
  • 02Readable output: separator whitespace joins neighbouring changes; deletions precede insertions
  • 03Inline or side-by-side (container-query stacked) layouts
  • 04Transition on every new pair: strikes draw first, then insertions type in with a stagger
  • 05Show changes off collapses deletions to the clean new text
  • 06"+12 −5" summary, Show changes switch and a replay button
motion · tweenmotion · staggerinput · clickinput · keyboard

05Accessibility

  • Changes use real <ins> and <del> elements with screen-reader labels ("[inserted: …]", "[deleted: …]").
  • Colour is never the only cue: insertions are underlined and deletions struck through.
  • With Show changes off, collapsed deletions are aria-hidden, so assistive tech reads just the new text.
  • The Show changes control is a role="switch" button; replay is a labelled button; both have visible focus rings.
  • A polite live region announces "5 words inserted, 3 words deleted" after each new diff.
  • prefers-reduced-motion shows each diff in its final state instantly.

06Limitations

  • Very large, very different texts (edit distance × length over ~4M) fall back to "replace everything".
  • Changed stretches render as inline blocks per word, so a character-level change inside a word can, rarely, wrap at the change.