Design · Components · Display

Diff Viewer

Code changes as unified or split diffs, line by line, on the kit's field panel.

TSexample.ts+5-3
1-function greet(name) {
2- console.log("Hello, " + name);
1+function greet(name: string): void {
2+ console.log(`Hello, ${name}!`);
3 }
4
5-greet("World");
5+// Call the function
6+greet("World");
7+greet("TypeScript");

Installation

npx shadcn@latest add @assistant-ui/diff-viewer

The @assistant-ui namespace resolves the Radix or Base UI flavor from your project's style through the style-aware registry entry in components.json. Without that entry, add by direct URL instead:

npx shadcn@latest add https://r.assistant-ui.com/base/diff-viewer.json

Usage

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

// With a unified diff patch
<DiffViewer patch={diffString} />

// With file comparison
<DiffViewer
  oldFile={{ content: "old content", name: "file.txt" }}
  newFile={{ content: "new content", name: "file.txt" }}
/>

As Markdown Language Override

Integrate with MarkdownTextPrimitive to render diff code blocks:

/components/assistant-ui/elements/markdown-text.tsx
import { DiffViewer } from "@/components/ui/diff-viewer";

const MarkdownTextImpl = () => {
  return (
    <MarkdownTextPrimitive
      remarkPlugins={[remarkGfm]}
      className="aui-md"
      components={defaultComponents}
      componentsByLanguage={{
        diff: {
          SyntaxHighlighter: ({ code }) => <DiffViewer patch={code} />
        },
      }}
    />
  );
};

export const MarkdownText = memo(MarkdownTextImpl);

Examples

Unified View

Shows all changes in a single column with +/- indicators. This is the default mode.

TSexample.ts+5-3
1-function greet(name) {
2- console.log("Hello, " + name);
1+function greet(name: string): void {
2+ console.log(`Hello, ${name}!`);
3 }
4
5-greet("World");
5+// Call the function
6+greet("World");
7+greet("TypeScript");
<DiffViewer patch={diffString} viewMode="unified" />

Split View

Shows old content on the left, new content on the right side-by-side.

TSexample.ts+5-3
1-function greet(name) {
1+function greet(name: string): void {
2- console.log("Hello, " + name);
2+ console.log(`Hello, ${name}!`);
3 }
3 }
4
4
5-greet("World");
5+// Call the function
6+greet("World");
7+greet("TypeScript");
<DiffViewer patch={diffString} viewMode="split" />

Interactive Mode Toggle

TSexample.ts+5-3
1-function greet(name) {
2- console.log("Hello, " + name);
1+function greet(name: string): void {
2+ console.log(`Hello, ${name}!`);
3 }
4
5-greet("World");
5+// Call the function
6+greet("World");
7+greet("TypeScript");

Variants

default
TSexample.ts+1-1
-let x = 1;
+const x = 1;
ghost
TSexample.ts+1-1
-let x = 1;
+const x = 1;
muted
TSexample.ts+1-1
-let x = 1;
+const x = 1;

Sizes

sm
TSexample.ts+1-1
-let x = 1;
+const x = 1;
default
TSexample.ts+1-1
-let x = 1;
+const x = 1;
lg
TSexample.ts+1-1
-let x = 1;
+const x = 1;

Theming

Changed lines carry a 2px gutter rule and a faint fill; the diff hue otherwise appears only on the +/- markers and the header stats, while code stays in ink. Override the CSS variables to retheme:

[data-slot="diff-viewer"] {
  --diff-add-bg: color-mix(in oklab, var(--color-green-500) 8%, transparent);
  --diff-add-rule: var(--color-green-500);
  --diff-add-text: var(--color-green-600);
  --diff-add-text-dark: var(--color-green-400);
  --diff-del-bg: color-mix(in oklab, var(--color-red-500) 8%, transparent);
  --diff-del-rule: var(--color-red-500);
  --diff-del-text: var(--color-red-600);
  --diff-del-text-dark: var(--color-red-400);
}
VariableDescription
--diff-add-bgFill for added lines
--diff-add-ruleGutter rule for added lines
--diff-add-textMarker and stat color for additions (light mode)
--diff-add-text-darkMarker and stat color for additions (dark mode)
--diff-del-bgFill for deleted lines
--diff-del-ruleGutter rule for deleted lines
--diff-del-textMarker and stat color for deletions (light mode)
--diff-del-text-darkMarker and stat color for deletions (dark mode)

API Reference

DiffViewer

The main component for rendering diffs.

DiffViewerProps
patch ?: string

Unified diff string (e.g., output from git diff).

code ?: string

Alias for patch (for markdown integration).

oldFile ?: { content: string; name?: string }

Old file for direct comparison.

newFile ?: { content: string; name?: string }

New file for direct comparison.

viewMode : "unified" | "split" = "unified"

Display mode for the diff.

variant : "default" | "ghost" | "muted" = "default"

Visual style variant.

size : "sm" | "default" | "lg" = "default"

Font size.

showLineNumbers : boolean = true

Show line numbers.

showIcon : boolean = false

Show file extension badge in header.

showStats : boolean = true

Show addition/deletion counts in header.

className ?: string

Additional CSS classes.

Composable API

ComponentDescription
DiffViewerMain component that renders the diff.
DiffViewerFileWrapper for each file in multi-file diffs.
DiffViewerHeaderFile name header with icon and stats.
DiffViewerContentScrollable content area.
DiffViewerLineIndividual line in unified mode.
DiffViewerSplitLineSide-by-side line pair in split mode.
DiffViewerFileBadgeFile extension badge (e.g., "TS").
DiffViewerStatsAddition/deletion count display.

Style Variants (CVA)

ExportDescription
diffViewerVariantsStyles for the root container.
diffLineVariantsBackground styles for diff lines.
diffLineTextVariantsText color styles for diff lines.
import {
  diffViewerVariants,
  diffLineVariants,
  diffLineTextVariants,
} from "@/components/ui/diff-viewer";

// Use variants directly
<div className={diffViewerVariants({ variant: "ghost", size: "sm" })}>
  Custom diff container
</div>

Utilities

ExportDescription
parsePatch(patch)Parse unified diff string into structured data.
computeDiff(old, new)Compute diff between two strings.
ParsedLineType for a single diff line.
ParsedFileType for a parsed file with lines and stats.
SplitLinePairType for a side-by-side line pair.

Styling

Data Attributes

Use data attributes for custom styling:

AttributeValuesDescription
data-slot"diff-viewer", "diff-viewer-header", "diff-viewer-line", etc.Component identification
data-type"add", "del", "normal", "empty"Line type
data-view-mode"unified", "split"Current view mode
data-variant"default", "ghost", "muted"Current variant

Custom CSS Example

[data-slot="diff-viewer"][data-view-mode="split"] {
  /* Custom split view styles */
}

[data-slot="diff-viewer-line"][data-type="add"] {
  /* Custom addition styles */
}

[data-slot="diff-viewer-line"][data-type="del"] {
  /* Custom deletion styles */
}