Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 23 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,26 @@ export function App() {

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `original` | `string` | - | Original text content |
| `modified` | `string` | - | Modified text content |
| `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 |
Expand Down Expand Up @@ -83,11 +100,11 @@ Metrics:
- initial render time
- memory usage (`usedJSHeapSize`)

Quick highlights from latest report:
Quick highlights from the latest report (`2026-04-08T06:45:43.686Z`):

- At `10k` lines: ~`60 FPS`, `187.2 ms` initial render, `9.5 MB` memory.
- At `50k/100k` lines: `react-diff-viewer` and `react-diff-viewer-continued` timeout.
- At `100k` lines: `141.1 MB` (`react-virtualized-diff`) vs `1297.0 MB` (`react-diff-view`).
- 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:

Expand Down
8 changes: 4 additions & 4 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,11 @@ export function App() {
- 首次渲染时间
- 内存占用(`usedJSHeapSize`)

最新结果亮点:
最新结果亮点(`2026-04-08T06:45:43.686Z`)

- `10k` 行:约 `60 FPS`,首渲染 `187.2 ms`,内存 `9.5 MB`。
- `50k/100k` 行:`react-diff-viewer` 与 `react-diff-viewer-continued` 超时
- `100k` 行:本库 `141.1 MB`,`react-diff-view` 为 `1297.0 MB`。
- `10k` 行(`react-virtualized-diff`):`60.8 FPS`,首渲染 `127.0 ms`,内存 `9.5 MB`。
- `50k/100k` 行:`react-diff-viewer` 与 `react-diff-viewer-continued` 均超时(单 case 超时 `60000 ms`)
- `100k` 行:本库 `104.0 MB`,`react-diff-view` 为 `1297.0 MB`。

运行方式:

Expand Down
189 changes: 189 additions & 0 deletions apps/demo/src/components/PlaygroundControls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@ export type DemoState = {
dataset: DatasetKey;
contextLines: number;
height: number;
splitView: boolean;
showDiffOnly: boolean;
hideLineNumbers: boolean;
useCompatApi: boolean;
enableRenderContent: boolean;
enableHighlight: boolean;
compareMethod: 'LINES' | 'WORDS' | 'CHARS';
disableWordDiff: boolean;
useDarkTheme: boolean;
linesOffset: number;
useCustomFoldRenderer: boolean;
};

type PlaygroundControlsProps = {
Expand Down Expand Up @@ -82,6 +93,184 @@ export default function PlaygroundControls(props: PlaygroundControlsProps) {
/>
</div>

<div className="control-panel__section">
<label className="toggle-row" htmlFor="splitView">
<input
id="splitView"
type="checkbox"
checked={value.splitView}
onChange={(event) =>
onChange({
...value,
splitView: event.target.checked,
})
}
/>
<span>Split view (`splitView`)</span>
</label>

<label className="toggle-row" htmlFor="showDiffOnly">
<input
id="showDiffOnly"
type="checkbox"
checked={value.showDiffOnly}
onChange={(event) =>
onChange({
...value,
showDiffOnly: event.target.checked,
})
}
/>
<span>Show diff only (`showDiffOnly`)</span>
</label>

<label className="toggle-row" htmlFor="hideLineNumbers">
<input
id="hideLineNumbers"
type="checkbox"
checked={value.hideLineNumbers}
onChange={(event) =>
onChange({
...value,
hideLineNumbers: event.target.checked,
})
}
/>
<span>Hide line numbers (`hideLineNumbers`)</span>
</label>

<label className="toggle-row" htmlFor="useCompatApi">
<input
id="useCompatApi"
type="checkbox"
checked={value.useCompatApi}
onChange={(event) =>
onChange({
...value,
useCompatApi: event.target.checked,
})
}
/>
<span>Use legacy `oldValue/newValue` props</span>
</label>

<label className="toggle-row" htmlFor="enableRenderContent">
<input
id="enableRenderContent"
type="checkbox"
checked={value.enableRenderContent}
onChange={(event) =>
onChange({
...value,
enableRenderContent: event.target.checked,
})
}
/>
<span>Enable `renderContent` formatter</span>
</label>

<label className="toggle-row" htmlFor="enableHighlight">
<input
id="enableHighlight"
type="checkbox"
checked={value.enableHighlight}
onChange={(event) =>
onChange({
...value,
enableHighlight: event.target.checked,
})
}
/>
<span>Enable `highlightLines` preset</span>
</label>
</div>


<div className="control-panel__section">
<label className="field-label" htmlFor="compareMethod">
Compare method
</label>
<select
id="compareMethod"
className="field-input"
value={value.compareMethod}
onChange={(event) =>
onChange({
...value,
compareMethod: event.target.value as DemoState['compareMethod'],
})
}
>
<option value="LINES">LINES</option>
<option value="WORDS">WORDS</option>
{/* <option value="CHARS">CHARS</option> */}
</select>

<label className="toggle-row" htmlFor="disableWordDiff">
<input
id="disableWordDiff"
type="checkbox"
checked={value.disableWordDiff}
onChange={(event) =>
onChange({
...value,
disableWordDiff: event.target.checked,
})
}
/>
<span>Disable word diff (`disableWordDiff`)</span>
</label>

<label className="toggle-row" htmlFor="useDarkTheme">
<input
id="useDarkTheme"
type="checkbox"
checked={value.useDarkTheme}
onChange={(event) =>
onChange({
...value,
useDarkTheme: event.target.checked,
})
}
/>
<span>Use dark theme (`useDarkTheme`)</span>
</label>

<label className="toggle-row" htmlFor="useCustomFoldRenderer">
<input
id="useCustomFoldRenderer"
type="checkbox"
checked={value.useCustomFoldRenderer}
onChange={(event) =>
onChange({
...value,
useCustomFoldRenderer: event.target.checked,
})
}
/>
<span>Custom fold renderer (`codeFoldMessageRenderer`)</span>
</label>

<label className="field-label" htmlFor="linesOffset">
Line offset: {value.linesOffset}
</label>
<input
id="linesOffset"
className="field-range"
type="range"
min={0}
max={200}
step={1}
value={value.linesOffset}
onChange={(event) =>
onChange({
...value,
linesOffset: Number(event.target.value),
})
}
/>
</div>

<button type="button" className="button button--primary button--block" onClick={onRunStressTest}>
Run 100k stress test
</button>
Expand Down
Loading
Loading