react-virtualized-diff
Built for large files: a virtualized React diff component.
At a Glance
- ⚡ Focus: fast diff rendering for very large files (10k+ lines).
- 🧩 Best for: embedded PR/MR review pages, long-list diff UIs, and internal review tools.
- 🧭 Positioning details: docs/positioning.md.
Positioning
Target scenarios: very large files, long-list scrolling experiences, and embedded code review pages (PR/MR diff views) where stable first paint and smooth scrolling are critical.
| Option |
Performance (large files) |
Bundle size |
Customizability |
React ecosystem integration cost |
react-virtualized-diff |
High (virtualized rendering for 10k+ lines) |
Small-to-medium (focused diff capability) |
High (style slots, render hooks, interaction callbacks) |
Low (native React API, TypeScript-friendly) |
| Traditional non-virtualized diff components |
Medium-to-low (often degrades on big files) |
Medium |
Medium (limited extension points) |
Low (quick to adopt, but needs extra optimization for large files) |
| In-house diff + virtualization |
Depends on implementation (can be high) |
Uncertain (often grows with business logic) |
High (fully controllable) |
High (build + maintenance overhead) |
High-performance React code/text diff viewer for large text/code files with virtualized rendering.
Quick Demo View

Why react-virtualized-diff
Most diff components are smooth for small files, but degrade heavily with 10k+ lines.
react-virtualized-diff is built for:
- Virtualized rendering for big-file performance
- Side-by-side layout for easy review
- Collapsed unchanged blocks with configurable context
- TypeScript support for predictable integration
Installation
pnpm add react-virtualized-diff
# or npm i react-virtualized-diff
# or yarn add react-virtualized-diff
Quick Start
import { SimpleDiffViewer } from 'react-virtualized-diff';
const original = `line 1\nline 2\nline 3`;
const modified = `line 1\nline 2 changed\nline 3\nline 4`;
export function App() {
return (
<SimpleDiffViewer
original={original}
modified={modified}
contextLines={2}
height={480}
/>
);
}
API
API tiering guide: docs/api-tiering.md.
SimpleDiffViewer (recommended for 80% of cases)
| Prop |
Type |
Default |
Description |
original |
string |
- |
Original text content (left) |
modified |
string |
- |
Modified text content (right) |
height |
number | string |
500 |
Viewport height of the virtual list |
splitView |
boolean |
true |
true for side-by-side, false for unified/inline |
showDiffOnly |
boolean |
true |
Show only changed lines with collapsible unchanged blocks |
contextLines |
number |
2 |
Number of unchanged lines kept around diff hunks |
hideLineNumbers |
boolean |
false |
Hide line number columns |
useDarkTheme |
boolean |
false |
Built-in dark theme |
locale |
DiffViewerLocale |
- |
UI text localization |
DiffViewer / AdvancedDiffViewer (deep customization)
| Prop |
Type |
Default |
Description |
original |
string |
- |
Original text content (new API) |
modified |
string |
- |
Modified text content (new API) |
oldValue |
string |
- |
Compatibility API: same as original |
newValue |
string |
- |
Compatibility API: same as modified |
splitView |
boolean |
true |
true for side-by-side, false for unified/inline |
showDiffOnly |
boolean |
true |
Show only changed lines with collapsible unchanged blocks |
contextLines |
number |
2 |
Number of unchanged lines kept around diff hunks |
extraLinesSurroundingDiff |
number |
- |
Compatibility API alias for context lines |
hideLineNumbers |
boolean |
false |
Hide line number columns |
highlightLines |
Array<'L-n' | 'R-n' | range> |
- |
Highlight specific lines (L-3, R-5, L-10-15) |
onLineNumberClick |
(lineId) => void |
- |
Called when a line number is clicked |
renderContent |
(line: string) => ReactNode |
- |
Custom line content renderer (syntax highlighting etc.) |
compareMethod |
"CHARS" | "WORDS" | "WORDS_WITH_SPACE" | "LINES" | "TRIMMED_LINES" | "SENTENCES" | "CSS" |
"LINES" |
Diff compare strategy |
disableWordDiff |
boolean |
false |
Disable inline word-level diff highlighting |
leftTitle |
ReactNode |
- |
Left pane title (split view) |
rightTitle |
ReactNode |
- |
Right pane title (split view) |
linesOffset |
number |
0 |
Add offset to displayed line numbers |
useDarkTheme |
boolean |
false |
Built-in dark theme |
styles |
Partial<DiffViewerStyles> |
- |
Override style slots |
codeFoldMessageRenderer |
({ hiddenCount, expanded }) => ReactNode |
- |
Custom fold button content renderer |
height |
number | string |
500 |
Viewport height of the virtual list |
locale |
DiffViewerLocale |
- |
UI text localization |
language |
string |
- |
Reserved field for future language-related extensions |
DiffViewerLocale
| Field |
Type |
Description |
collapse |
string |
Label for collapse button |
showMoreLines |
(count: number) => string |
Label factory for “show hidden lines” |
Benchmark
Included benchmark compares:
react-virtualized-diff
react-diff-viewer
react-diff-viewer-continued
react-diff-view
Dataset sizes: 1k / 10k / 50k / 100k lines.
Metrics:
- FPS (during auto-scroll)
- initial render time
- memory usage (
usedJSHeapSize)
Quick highlights from the latest report (2026-04-08T06:45:43.686Z):
- At
10k lines (react-virtualized-diff): 60.8 FPS, 127.0 ms initial render, 9.5 MB memory.
- At
50k/100k lines: react-diff-viewer and react-diff-viewer-continued both timeout (60000 ms per case).
- At
100k lines: 104.0 MB (react-virtualized-diff) vs 1297.0 MB (react-diff-view).
Run benchmark:
pnpm install
pnpm benchmark
Demo
pnpm install
pnpm dev
Monorepo Structure
apps/demo/ # Vite demo app
apps/benchmark/ # benchmark app
packages/react/ # npm package source
Changelog
See CHANGELOG.md.
License
MIT
评论