DiffView
StableReview the changes an assistant made to your files, and keep or reject each one.
Also known as diff viewer, code diff, compare changes
Install with npm install @tattva/ui (setup), or copy the code from the kit in Downloads.
Review two files
- 1 line added, 1 removed
- 1 line added, 0 removed
Hunk 1 @@ -4,5 +4,6 @@
2 files left to review
<DiffView files={files} />Decide per file, per change, or use the bottom bar. Next file jumps to a file you have not finished.
When to use it
The place to decide what stays after an assistant edits files. Files are listed on one side and the chosen file's changes on the other, so people can decide one group of lines, a whole file, or everything at once.
Made of: File list (one row per file) · Change heading with Keep and Reject · Changed lines (old number, new number, sign, text) · Bottom bar (files left, Keep all, Reject all, Next file) · Spoken update
Use it for
- Reviewing edits an assistant made before they are saved or shared.
- Letting people keep some changes in a file and reject others.
- Working through many changed files, with a count of how many are left.
Not for
- A short summary of changed files without the lines: use DiffFileRow.
- Showing code that is not a change: use CodeBlock.
- Going back to an earlier saved version of the whole work: use VersionHistory.
Do and avoid
- 1 line added, 1 removed
Hunk 1
1 file left to review
- 1 line added, 1 removed
Hunk 1
1 file left to review
Do
- Show how many files are left, so people know how much remains.
- Give Keep all and Reject all the same visual weight.
- Save people's choices in your app if the review must survive a page reload.
- Show real line numbers so people can find each change.
Avoid
- Don't mark lines with only color. Keep the plus and minus signs.
- Don't offer Keep all without showing how many files it affects. Changes already decided stay as they are.
- Don't use it to compare long paragraphs of writing. It shows lines in a fixed-width font.
On a phone
- Below 768px wide the file list sits above the changes instead of beside them.
- Each block of lines scrolls sideways on its own, because the lines do not wrap, and the two line-number columns use 96px of a phone screen.
- Keep, Reject and Undo buttons keep their size and have a 44px tap area on a touch screen, and the footer buttons wrap onto more rows.
Seen in products
- Cursor has a Review tab with a file list, per-file Keep and Reject, a bottom bar with Keep All and Reject All, and Review Next File navigation (from a search snippet plus community threads).
- Cursor community feedback asks for per-change (hunk) apply, which suggests a recent regression. DiffView offers per-hunk Keep and Reject for that reason.
- Windsurf lets you accept or reject proposed modifications.
- GitHub Copilot coding agent has the diff reviewed on a branch before the pull request is created.
- Vercel AI Elements ships a Commit component.
Properties
| Prop | Type | Default | Description |
|---|---|---|---|
| filesrequired | DiffFile[] | none | Files to review. Each has id, path and hunks. Counts are computed from the hunk lines. |
| statuses | Record<string, DiffReviewState> | none | Review state per file id, for controlled use. Missing ids are pending. |
| defaultStatuses | Record<string, DiffReviewState> | none | Initial states when uncontrolled. |
| onStatusesChange | (next: Record<string, DiffReviewState>) => void | none | Called after any decision at file or hunk level. File states are derived from the hunks, so a mixed file reports "partial". |
| hunkStatuses | Record<string, DiffHunkState[]> | none | Decision per hunk, by file id then hunk index, for controlled use. Missing entries follow the file's kept or rejected decision, or pending. |
| defaultHunkStatuses | Record<string, DiffHunkState[]> | none | Initial hunk decisions when uncontrolled. |
| onHunkStatusesChange | (next: Record<string, DiffHunkState[]>) => void | none | Called after any decision, with the decisions for every hunk. |
| onHunkChange | (fileId: string, hunkIndex: number, state: DiffHunkState) => void | none | Called when one hunk's Keep, Reject or Undo is chosen. The index starts at 0. |
| activeId | string | none | File whose hunks are shown, for controlled use. |
| defaultActiveId | string | none | Initial file when uncontrolled. Defaults to the first file. |
| onActiveChange | (id: string) => void | none | Called when the shown file changes. |
| labels | DiffViewLabels | none | Replace the visible text, including the remaining-count function, the hunk title function, the keep, reject, kept, rejected, partial and undo words, and announceFile, announceHunk and announceAll, the sentences screen readers hear after each decision. |
| className | string | none | Extra classes on the outer section. |
"Show code under the preview gives the code for the example on the stage."
States
States this part has beyond its default look. Build each one your screen can reach. States set by a property are in the Code tab.
- pending
- A file or change block with no decision yet shows Keep and Reject buttons.
- kept
- After Keep, the block shows a green Kept badge and an Undo button.
- rejected
- After Reject, the block shows a red Rejected badge, an Undo button and dimmed lines.
- partly kept
- A file with some blocks kept and others undecided shows as partly kept in the file list.
- all reviewed
- When nothing is left to review, the footer says so and Keep all, Reject all and Next file are disabled.
- empty
- With no files, only the line No changes to review is shown.
- wide lines
- Long lines scroll sideways inside each block, which can be focused with the keyboard.
Tokens
Change one of these and every part that uses it follows.
--success-soft--success-fg--danger-soft--danger-fg--surface-sunken--border--radius-card
Built in
- Added and removed lines show a plus or minus sign, and screen readers hear "Added:" or "Removed:" before the text.
- Line numbers are hidden from screen readers so they do not interrupt the text.
- Every Keep, Reject and Undo button names the change and the file, for example "Keep hunk 2 in notes/trip-plan.txt".
- Screen readers hear each choice, and how many files are left to review.
- Each group of lines can be reached with Tab and scrolled sideways, so long lines can be read.
What you need to do
- If you translate the labels, also pass hunk, remaining, announceFile, announceHunk and announceAll, because they are spoken in button names and updates.
Built toward WCAG 2.2 AA and checked automatically; no independent audit yet. Read the accessibility guide