# React Diff Viewer für React 19: Split, Unified, Wort-Diffs

> Wie ein React Diff Viewer arbeitet, welche Pakete unter React 19 laufen und wie du KI-Änderungen, Verträge und Configs mit Split-Ansicht und Wort-Diff prüfst.

- Autor: [Serban Rusu](https://wingo-ui.com/blog/authors/serban), Founder of Wingo UI
- Veröffentlicht: 16. Okt. 2026
- Kategorie: [Komponenten-Guides](https://wingo-ui.com/de/blog/category/components)
- Lesezeit: 10 Min.
- Kanonische URL: https://wingo-ui.com/de/blog/react-diff-viewer
- Englische Fassung: https://wingo-ui.com/blog/react-diff-viewer

## Kurz gesagt

Ein React Diff Viewer vergleicht zwei Strings erst Zeile für Zeile und dann Wort für Wort innerhalb jedes geänderten Zeilenpaars, und er zeigt das Ergebnis auf breiten Bildschirmen nebeneinander und auf Smartphones in einer einzigen Spalte. Das ursprüngliche Paket react-diff-viewer erschien zuletzt im Mai 2020 und unterstützt nur React 15 und 16; unter React 19 nimmst du react-diff-viewer-continued für zwei Strings, react-diff-view für Git-Patches oder eine Komponente als Quellcode wie den Diff Viewer von Wingo UI (Pro). Bevor du einen ausrollst, teste Whitespace-Rauschen, Zeilenenden, deine größte echte Datei und die Tastaturbedienung.

Du hast zwei Versionen eines Textes, und jemand muss sehen, was sich geändert hat: die Datei, die ein Agent umgeschrieben hat, ein Vertrag, der mit Änderungen vom Kunden zurückkam, eine Config, die jemand in die Produktion schieben will. Ein React Diff Viewer sollte das auf einen Blick beantworten. Ein naiver Viewer färbt ganze Zeilen rot und grün, markiert jedes Leerzeichen am Zeilenende, quetscht zwei Spalten auf ein Smartphone und blockiert bei einer großen Datei den Main Thread. Dieser Guide zeigt, wie ein Diff Viewer funktioniert, worauf du bei einer Alternative zu react-diff-viewer achten solltest und wie du einen für KI-Änderungen, Verträge und Configs einbaust. Die Beispiele nutzen den [Diff Viewer](https://wingo-ui.com/components/diff-viewer) von Wingo UI, der Bibliothek, die wir bauen. Wir sind also nicht neutral; bei Fakten zu anderen Paketen verlinken wir die Quellen.

## Wie funktioniert ein React Diff Viewer?

Eine Text-Diff-Komponente in React, die zwei Strings bekommt, arbeitet in zwei Durchgängen. Zuerst kommt ein Zeilen-Diff: Sie teilt beide Texte in Zeilen und sucht die kleinste Menge an entfernten und hinzugefügten Zeilen, die den einen Text in den anderen überführt (der Myers-Algorithmus, den jsdiff umsetzt und den Git standardmäßig nutzt). Danach ist jede Zeile unverändert, entfernt oder hinzugefügt. Dann folgt für jede entfernte Zeile, der eine hinzugefügte gegenübersteht, ein zweiter Diff innerhalb des Paars, nach Wort oder nach Zeichen. So sieht man, dass aus „15 days“ „30 calendar days“ wurde, statt eine komplett rote und eine komplett grüne Zeile vor sich zu haben. Genau diesen zweiten Durchgang lassen die meisten Snippets für Wort-Diffs in React weg, und genau ihn lesen Reviewer tatsächlich.

Der Viewer von Wingo UI nutzt [jsdiff](https://github.com/kpdecker/jsdiff) (das Paket `diff`, Version 9) für beide Durchgänge. Er bildet Paare aus entfernten und hinzugefügten Zeilen und markiert Wörter nur, wenn ein Paar noch mehr als 40 % seines Textes gemeinsam hat. Darunter sind die Wörter ohnehin größtenteils verschieden, und die eingefärbte Zeile allein liest sich besser als Konfetti. Unveränderte Zeilen, die mehr als `contextLines` (Standard 3) von einer Änderung entfernt sind, klappen zu einer Zeile zusammen, die anzeigt, wie viele Zeilen sich dort verbergen.

Probier es im Standard-Playground aus: Version 3 und Version 4 eines Dienstleistungsvertrags.

> Live-Demo (Diff Viewer): Überflieg die beiden Versionen des Vertrags: Gebühr, Nutzerzahl und Zahlungsbedingungen haben sich geändert. Drück die Pfeile oder Alt+Pfeil nach unten, um zur nächsten Änderung zu springen, und tipp dann auf eine eingeklappte Zeile, um die verborgenen Zeilen anzuzeigen. Probier sie unter [Diff Viewer](https://wingo-ui.com/components/diff-viewer) aus und installier sie mit `npx wingo-ui@latest add diff-viewer`.

## Welche Alternative zu react-diff-viewer solltest du nutzen?

Unter React 19 nimmst du react-diff-viewer-continued für zwei Strings mit der ursprünglichen API, react-diff-view für Git-Patches und eine Komponente als Quellcode, wenn der Viewer zu deinem Design System passen muss. Das Paket, das die meisten Tutorials noch importieren, `react-diff-viewer`, hat seit 2020 kein Release mehr bekommen und nennt React 17 oder neuer nicht als Peer Dependency. Alle Optionen unten zeigen die Side-by-Side-Ansicht, die du von Pull Requests auf GitHub kennst, und dazu eine Unified-Ansicht. Das sagt die npm-Registry über sie, geprüft am 16. Oktober 2026:

| Paket | Neueste Version | Letzte Veröffentlichung | React-Peer-Bereich | Eingabe | Lizenz |
|---|---|---|---|---|---|
| [react-diff-viewer](https://www.npmjs.com/package/react-diff-viewer) | 3.1.1 | 22. Mai 2020 | 15 oder 16 | zwei Strings | MIT |
| [react-diff-viewer-continued](https://www.npmjs.com/package/react-diff-viewer-continued) | 4.4.0 | 14. Juli 2026 | 15 bis 19 | zwei Strings oder Objekte für JSON | MIT |
| [react-diff-view](https://www.npmjs.com/package/react-diff-view) | 3.3.3 | 30. März 2026 | ab 16.14 | Git-Unified-Diff als Text | MIT |
| [Wingo UI Diff Viewer](https://wingo-ui.com/components/diff-viewer) | Registry-Eintrag | kein npm-Paket | 19 | zwei Strings | Pro-Lizenz |

Wann welche Option die bessere Wahl ist:

- **react-diff-viewer-continued** ist der direkte Ersatz, wenn du die ursprüngliche API schon nutzt. Laut seiner [README](https://github.com/Aeolun/react-diff-viewer-continued) ist es ein Fork des Originals mit aktualisierten Abhängigkeiten für moderne Stacks. Das Styling läuft über Emotion (CSS-in-JS), standardmäßig vergleicht es Zeichen für Zeichen, und die Option `infiniteLoading` virtualisiert lange Diffs, sodass nur die sichtbaren Zeilen gerendert werden. Nimm es, wenn du ein kostenloses npm-Paket willst, seine Vergleichsmethoden für JSON und YAML brauchst oder Diffs mit Zehntausenden Zeilen anzeigen musst.
- **[react-diff-view](https://github.com/otakustay/react-diff-view)** ist das richtige Werkzeug, wenn deine Eingabe ein Git-Patch ist und nicht zwei Texte. Laut README ist es eine Komponente, die die Unified-Diff-Ausgabe von Git verarbeitet, mit `parseDiff` für Patches über mehrere Dateien und einem Widget-System für Code-Kommentare. Für einen Pull Request mit 40 geänderten Dateien fängst du dort an.
- **Eine Komponente als Quellcode** passt, wenn der Viewer zu deinem Design System passen, deine Theme-Tokens lesen und sich auf dem Smartphone wie der Rest deiner UI verhalten muss. Genau diesen Fall deckt der Diff Viewer von Wingo UI ab.

## Wie installiere ich den Diff Viewer und rendere einen Diff?

Installier die Komponente mit der CLI und gib ihr dann zwei Strings.

```bash
npx wingo-ui@latest add diff-viewer
```

Der Befehl kopiert `components/ui/diff-viewer.tsx` samt den kostenlosen Komponenten Button, Badge, Copy Button, Dropdown Menu, Segmented Control und Tooltip, die sie importiert, und installiert `diff`, `prism-react-renderer`, `motion` und `lucide-react`. Der Diff Viewer gehört zu [Wingo UI Pro](https://wingo-ui.com/de/pricing), führ also vorher `npx wingo-ui@latest login` aus.

Die beiden Pflicht-Props sind die Texte. Titel benennen die Spalten:

```tsx
import { DiffViewer } from "@/components/ui/diff-viewer";

export function ContractChanges({ v3, v4 }: { v3: string; v4: string }) {
  return (
    <DiffViewer
      oldValue={v3}
      newValue={v4}
      oldTitle="Vertrag v3 · 12. Sep. 2026"
      newTitle="Vertrag v4 · 24. Sep. 2026"
    />
  );
}
```

Die Datei ist eine Client-Komponente, aber du kannst sie aus einer Server-Komponente rendern, solange du nur serialisierbare Props wie diese übergibst.

## Welche Props des Diff Viewers sind am wichtigsten?

Für die meisten Reviews reichen die beiden Texte. Diese Props bestimmen, was die prüfende Person sieht und wie viel davon:

- **`defaultView`** (`"auto"`, `"split"` oder `"unified"`, Standard `"auto"`). Auto zeigt ab 768px die Ansicht nebeneinander und darunter sowie auf dem Server eine einzelne Spalte. Dabei prüft es auch die Breite des Viewers selbst, sodass ein schmales Panel auf dem Desktop ebenfalls die einzelne Spalte bekommt. Steuern kannst du das mit `view` und `onViewChange`.
- **`granularity`** (`"word"`, `"char"` oder `"none"`, Standard `"word"`). Wörter passen zu Fließtext und Code. Zeichen passen zu kurzen Änderungen wie Produkttiteln, Preisen oder IDs.
- **`contextLines`** (Standard 3). Wie viele unveränderte Zeilen um jede Änderung herum sichtbar bleiben. `-1` zeigt alles.
- **`ignoreWhitespace`** und **`ignoreCase`** (beide false). Unterschiede nur im Whitespace oder nur in der Groß- und Kleinschreibung zählen als unverändert. Beide findest du auch im Menü der Toolbar.
- **`language`** (Standard leer). Eine Prism-Sprache wie `tsx`, `json` oder `yaml` ergänzt Syntaxfarben aus den Tone-Tokens, damit beide Themes lesbar bleiben.
- **`wrap`** (Standard false), **`maxHeight`** (Standard 560, `0` wächst mit dem Inhalt), **`size`** (`sm` oder `md`) und **`radius`**.
- **`labels`**: alle sichtbaren und vorgelesenen Texte, standardmäßig auf Englisch, damit du sie übersetzen kannst.

Diese zweite Demo startet in der Unified-Ansicht mit Markierungen pro Zeichen und einer Kontextzeile. Daran siehst du, wie viel kompakter sich die Änderung auf Zeichenebene liest.

> Live-Demo (Diff Viewer): Unified-Ansicht mit Markierungen pro Zeichen: Vergleiche die Zeile zur Zahlung, in der nur die geänderten Zeichen markiert sind. Öffne das Menü, um den Zeilenumbruch einzuschalten oder Groß- und Kleinschreibung zu ignorieren. Probier sie unter [Diff Viewer](https://wingo-ui.com/components/diff-viewer) aus und installier sie mit `npx wingo-ui@latest add diff-viewer`.

## Wie prüfe ich KI-Änderungen mit einem Diff Viewer?

Render die aktuelle Datei und den Vorschlag des Agenten in einem Viewer, mit Übernehmen und Ablehnen darunter. So landet keine Änderung ungeprüft auf der Platte:

```tsx
"use client";

import { Check, X } from "lucide-react";
import { Button } from "@/components/ui/button";
import { DiffViewer } from "@/components/ui/diff-viewer";

type Proposal = { path: string; current: string; proposed: string };

export function AgentEditReview({
  proposal,
  onAccept,
  onReject,
}: {
  proposal: Proposal;
  onAccept: () => void;
  onReject: () => void;
}) {
  return (
    <div className="flex flex-col gap-3">
      <DiffViewer
        oldValue={proposal.current}
        newValue={proposal.proposed}
        oldTitle={`${proposal.path} · aktuell`}
        newTitle={`${proposal.path} · vorgeschlagen`}
        language="tsx"
        maxHeight={480}
      />
      <div className="flex justify-end gap-2">
        <Button variant="ghost" leftIcon={<X />} onClick={onReject}>
          Ablehnen
        </Button>
        <Button tone="brand" leftIcon={<Check />} onClick={onAccept}>
          Änderung übernehmen
        </Button>
      </div>
    </div>
  );
}
```

Agenten liefern oft eine komplett neu geschriebene Datei zurück, um vier Zeilen zu ändern. Mit dem Standardwert 3 für `contextLines` sieht die prüfende Person diese vier Zeilen und ihre Nachbarn, der Rest verschwindet in aufklappbaren Zeilen. Für alles andere in einem Agent-Lauf, also Schritte, Freigaben und Fehler, lies unseren Guide zur [Tool Call UI für KI-Agenten](https://wingo-ui.com/de/blog/ai-agent-tool-call-ui).

## Wie prüfe ich Config-Änderungen ohne Whitespace-Rauschen?

Setz `ignoreWhitespace` und gib die Sprache mit. In YAML- und Env-Dateien sammeln sich Leerzeichen am Zeilenende und doppelte Leerzeichen, die niemanden interessieren, und mit dem Flag zählen diese Zeilen als unverändert:

```tsx
<DiffViewer
  oldValue={productionYaml}
  newValue={proposedYaml}
  oldTitle="config.yaml · Produktion"
  newTitle="config.yaml · Vorschlag"
  language="yaml"
  ignoreWhitespace
  contextLines={1}
/>
```

Das Flag fasst Folgen von Leerzeichen und Tabs zusammen und trimmt jede Zeile vor dem Vergleich. Außerdem ignoriert es einen fehlenden Zeilenumbruch am Dateiende (ohne das Flag zählt ein fehlender Umbruch als Änderung in der letzten Zeile, wie in Git). Zeilenenden werden immer normalisiert, damit eine unter Windows gespeicherte Datei nicht jede Zeile aufleuchten lässt. Unterscheiden sich nur die Zeilenenden, sagt der Viewer das, statt einen leeren Diff zu zeigen. Bei strukturierten Payloads, in denen Reihenfolge der Schlüssel und Verschachtelung mehr zählen als Zeilen, ist ein Baum wie der [JSON Viewer](https://wingo-ui.com/components/json-viewer) oft die bessere Ansicht.

## Wie füge ich Zeilenkommentare zu einem Diff hinzu?

`onLineClick` macht aus den Zeilennummern Buttons, und `renderGutter` ergänzt hinter den Nummern eine Zelle für Zähler, Avatare oder Markierungen zur Testabdeckung:

```tsx
"use client";

import { useState } from "react";
import { DiffViewer, type DiffLine, type DiffSide } from "@/components/ui/diff-viewer";

type Note = { line: number; side: DiffSide; text: string };

export function ReviewWithComments({ before, after }: { before: string; after: string }) {
  const [notes, setNotes] = useState<Note[]>([]);
  const [target, setTarget] = useState<{ line: number; side: DiffSide } | null>(null);

  const lineOf = (line: DiffLine, side: DiffSide) => (side === "new" ? line.newNumber : line.oldNumber);

  return (
    <>
      <DiffViewer
        oldValue={before}
        newValue={after}
        onLineClick={(line, side) => {
          const n = lineOf(line, side);
          if (n) setTarget({ line: n, side });
        }}
        renderGutter={(line, side) => {
          const count = notes.filter((note) => note.side === side && note.line === lineOf(line, side)).length;
          return count ? <span className="text-[10px] tabular-nums text-muted-foreground">{count}</span> : null;
        }}
      />
      {target ? (
        <p className="text-sm">
          Kommentar zu Zeile {target.line} ({target.side === "new" ? "neue Version" : "Original"})
        </p>
      ) : null}
    </>
  );
}
```

Häng dein eigenes Kommentarfeld an `target` und schieb beim Absenden einen Eintrag in `notes`. Wenn du nachvollziehen willst, wer was wann geändert hat, kombinier den Viewer mit einem [Audit Log](https://wingo-ui.com/components/audit-log) und öffne den Diff aus jedem Eintrag.

## Ist der Diff Viewer barrierefrei und auf dem Smartphone nutzbar?

Ja. Änderungen sind nicht nur farbig markiert, sondern tragen auch Vorzeichen und vorgelesene Labels, du springst per Tastatur zwischen Änderungen, und auf dem Smartphone bekommst du eine einzelne Spalte mit 44px großen Touch-Zielen:

- **Vorzeichen und vorgelesene Labels.** Entfernte Zeilen tragen ein Minus, hinzugefügte ein Plus, und jede geänderte Zeile hat ein vorgelesenes Label „added“ oder „removed“. Die Statistik-Badges (`+12 −3`) werden als „12 additions, 3 deletions“ vorgelesen.
- **Tastatur.** Alt+Pfeil nach unten und Alt+Pfeil nach oben springen von überall im Viewer zur nächsten oder vorherigen Änderung, laufen an den Enden im Kreis, fokussieren die Änderung und sagen „Change 2 of 5“ an. Der scrollende Bereich ist fokussierbar, du kannst ihn also mit den Pfeiltasten scrollen.
- **Reduzierte Bewegung.** Das Aufblitzen der angesprungenen Änderung und die Überblendung zwischen den Ansichten entfallen, wenn die Person reduzierte Bewegung eingestellt hat.
- **Smartphones.** Unter 768px oder in einem Panel, das schmaler als 640px ist, wandert der Umschalter für die Ansicht ins Menü, und auf dem Smartphone öffnet sich dieses Menü als Bottom Sheet. Lange Zeilen scrollen seitlich, während die Zeilennummern stehen bleiben, oder sie brechen mit `wrap` um. Die Buttons der Toolbar und die eingeklappten Zeilen bekommen auf Touchscreens 44px große Trefferflächen.

## Woran scheitert ein Diff Viewer in der Produktion?

Die meisten dieser Probleme kommen von der Eingabe. Prüf sie, bevor du ausrollst:

- **Gerendertes HTML vergleichen.** Vergleiche den Quelltext eines Vertrags oder einer Markdown-Datei, nicht das HTML, das daraus entsteht. Tag-Salat macht aus einem geänderten Wort einen geänderten Absatz.
- **Riesige Dateien auf dem Main Thread.** Ein Diff läuft in JavaScript auf dem Main Thread, solange du ihn nicht in einen Worker verlagerst. Bei mehr als 2.000 Zeilen pro Seite verschiebt der Wingo-Viewer die Arbeit in einen Idle Callback und zeigt ein Skeleton, damit die Seite zuerst gezeichnet wird. Sind zwei große Texte sehr verschieden, gibt der Zeilen-Diff nach 2 Sekunden auf und zeigt einen einzigen ersetzten Block. Zeilen werden nicht virtualisiert, also halt `contextLines` bei großen Dateien niedrig und teste deine größte echte Datei.
- **Minifizierte Zeilen.** Eine einzelne JSON-Zeile mit 200 KB hat keinen sinnvollen Wort-Diff. Bei mehr als 20.000 Zeichen fällt der Viewer auf Zeilenmarkierungen zurück. Formatier das JSON vorher, wenn du mehr sehen willst.
- **Binäre Inhalte.** Bei Text mit NUL-Bytes erscheint ein Hinweis auf Binärdaten statt Zeichensalat.
- **Ein Diff, wo ein Snippet reicht.** Gibt es nur eine Version, nimm stattdessen einen [Code Block](https://wingo-ui.com/components/code-block) mit Kopierfunktion.

Wenn du die Zahlen ohne die UI brauchst, etwa für ein Badge „+4 −2“ in einer Liste von Revisionen, nutze `computeDiff(oldValue, newValue, options)`. Die Funktion wird aus derselben Datei exportiert und gibt `{ rows, stats, blocks, lineEndingsOnly, binary }` zurück. Sie ist eine ganz normale Funktion, ruf sie also in einer Client-Komponente mit `useMemo` oder in deinen Tests auf.

Weitere ausführliche Artikel zu einzelnen Komponenten findest du in den [Komponenten-Guides](https://wingo-ui.com/de/blog/category/components), oder fang mit unserem Pillar-Artikel zu [React-Formularkomponenten](https://wingo-ui.com/de/blog/react-form-components-guide) an. Wenn du kopierte Komponenten mit dem Upstream synchron hältst, zeigt unser Artikel über [das Aktualisieren von shadcn-Komponenten, ohne eigene Änderungen zu verlieren](https://wingo-ui.com/de/blog/update-shadcn-components-without-losing-edits) dieselbe Art von Diff, angewendet auf deine eigenen Quelldateien.

## Wie baue ich einen Diff Viewer in meine React-App ein?

Führ `npx wingo-ui@latest add diff-viewer` aus, gib ihm zwei Strings und lass die Standardwerte erst einmal, wie sie sind: automatische Ansicht, Wort-Markierungen und drei Kontextzeilen decken die meisten Reviews ab. Ergänze dann `language`, `ignoreWhitespace` oder `onLineClick`, wenn dein Fall sie braucht. Wenn du mit einem Coding-Agenten arbeitest, verbinde den [MCP-Server von Wingo UI](https://wingo-ui.com/docs/mcp), damit er die echten Props liest, statt sie zu raten.

## Komponenten in diesem Artikel

- [Diff Viewer](https://wingo-ui.com/components/diff-viewer) (Pro): A React diff viewer for two texts: split or unified views, word highlights, collapsed context, syntax colors and jumps between changes. Installation: `npx wingo-ui@latest add diff-viewer`
- [Code Block](https://wingo-ui.com/components/code-block) (Free): A React code block with syntax highlighting, line numbers, highlighted lines and a copy button, themed from a single accent color. Installation: `npx wingo-ui@latest add code-block`
- [JSON Viewer](https://wingo-ui.com/components/json-viewer) (Pro): A collapsible JSON tree with type colors, search that opens the matching branches, copy value or path, and virtualization for huge payloads. Installation: `npx wingo-ui@latest add json-viewer`
- [Audit Log](https://wingo-ui.com/components/audit-log) (Pro): A React audit log table: who did what, when and to which record, with filters, day groups, before / after diffs and a CSV export. Installation: `npx wingo-ui@latest add audit-log`

## Häufige Fragen

### Wie zeige ich in React den Unterschied zwischen zwei Strings an?

Gib beide Strings an eine Diff-Viewer-Komponente. Sie berechnet einen Zeilen-Diff und danach innerhalb jedes geänderten Zeilenpaars einen Wort- oder Zeichen-Diff. Mit Wingo UI ist das <DiffViewer oldValue={a} newValue={b} />, gebaut auf der Bibliothek jsdiff.

### Welche Alternative zu react-diff-viewer passt für React 19?

react-diff-viewer-continued behält die ursprüngliche API und führt React 19 in seinen Peer Dependencies (Stand Oktober 2026). Ist deine Eingabe die Ausgabe von git diff, nimm react-diff-view. Willst du den Quellcode in deinem Repo haben, gestylt mit Tailwind-Tokens, installier den Diff Viewer von Wingo UI.

### Soll ich einen Split- oder einen Unified-Diff nutzen?

Split, wenn genug Platz da ist: Der Viewer von Wingo UI zeigt ihn, wenn der Bildschirm mindestens 768px breit ist und der Viewer selbst mindestens 640px. Auf dem Smartphone bleiben bei zwei Spalten nur ein paar Zeichen pro Zeile, deshalb liest sich eine einzelne Spalte dort besser.

### Wie ignoriere ich Whitespace-Änderungen in einem React-Diff?

Vergleiche normalisierte Fassungen der Zeilen statt der Rohtexte, damit Zeilen, die sich nur in Leerzeichen oder Tabs unterscheiden, als unverändert zählen. Im Diff Viewer von Wingo UI setzt du dafür ignoreWhitespace oder schaltest es im Menü der Toolbar ein.

### Ist der Diff Viewer von Wingo UI kostenlos?

Nein. Der Diff Viewer gehört zu Wingo UI Pro. Button, Badge, Tooltip, Segmented Control und Copy Button, auf denen er aufbaut, sind kostenlos.

---

Quelle: https://wingo-ui.com/de/blog/react-diff-viewer
