Zero-dependency text diff utilities for TypeScript, plus optional React components for rendering line and character diffs.
# Using pnpm
pnpm add @ekaone/diff-kit
# Using npm
npm install @ekaone/diff-kit
# Using yarn
yarn add @ekaone/diff-kitReact is an optional peer dependency. Install it only when you use @ekaone/diff-kit/react.
pnpm add react react-domLine-by-line diff using an LCS algorithm.
import { diff } from "@ekaone/diff-kit";
const lines = diff("hello\nworld", "hello\nearth");
// [
// { type: "unchanged", content: "hello" },
// { type: "removed", content: "world" },
// { type: "added", content: "earth" },
// ]Groups changed lines into chunks with surrounding context.
import { diff, formatDiff } from "@ekaone/diff-kit";
const lines = diff(before, after);
const chunks = formatDiff(lines, { context: 3 });
for (const chunk of chunks) {
for (const line of chunk) {
const prefix =
line.type === "added" ? "+" : line.type === "removed" ? "-" : " ";
console.log(`${prefix} ${line.content}`);
}
}Summary counts from a line diff.
import { diff, diffStats } from "@ekaone/diff-kit";
const stats = diffStats(diff(before, after));
// { added: 2, removed: 1, unchanged: 5 }Character-by-character inline diff.
import { diffChars } from "@ekaone/diff-kit";
const chars = diffChars("prod", "product");
// [
// { type: "unchanged", char: "p" },
// { type: "unchanged", char: "r" },
// { type: "unchanged", char: "o" },
// { type: "unchanged", char: "d" },
// { type: "added", char: "u" },
// { type: "added", char: "c" },
// { type: "added", char: "t" },
// ]Import React components from the @ekaone/diff-kit/react entry point.
import { DiffViewer } from "@ekaone/diff-kit/react";
export function ReviewDiff() {
return (
<DiffViewer
before={"const status = \"draft\";"}
after={"const status = \"published\";"}
context={2}
/>
);
}Renders a chunked line-by-line diff. It shows a stats bar by default.
<DiffViewer before={before} after={after} context={3} showStats />Renders only the added, removed, and unchanged counts.
<DiffStats before={before} after={after} />Renders an inline character diff.
<DiffChars before="v1.4.0" after="v1.5.0-beta" />Renders one badge per character, useful when you want to inspect the raw character diff visually.
<DiffCharsRaw before="prod" after="product" />React components include inline styles by default. Pass theme to adjust those styles.
<DiffViewer
before={before}
after={after}
theme={{
added: { bg: "#ecfdf5", text: "#047857", border: "#10b981" },
removed: { bg: "#fef2f2", text: "#b91c1c", border: "#ef4444" },
}}
/>Pass classNames when you prefer Tailwind or your own CSS classes. When classNames is provided, diff token inline styles are skipped.
<DiffViewer
before={before}
after={after}
classNames={{
added: "bg-green-50 text-green-800",
removed: "bg-red-50 text-red-800 line-through",
unchanged: "text-neutral-500",
}}
/>type DiffType = "added" | "removed" | "unchanged";
interface DiffLine {
type: DiffType;
content: string;
}
interface DiffOptions {
context?: number;
}
interface DiffStats {
added: number;
removed: number;
unchanged: number;
}
interface CharDiff {
type: DiffType;
char: string;
}React prop types are exported from @ekaone/diff-kit/react: DiffViewerProps, DiffStatsProps, DiffCharsProps, DiffCharsRawProps, DiffTheme, and DiffClassNames.
See examples/README.md for runnable core and React examples.
MIT (c) Eka Prasetia
If this library helps you, please consider giving it a star on GitHub.