File Diff
Shows one file's changes as a diff, even while streaming.
Installation
pnpm dlx shadcn@latest add @paneui/file-diffUsage
Examples
Renderer
The bundled diff renderer draws the rows inside a shadow root, themed with this library's tokens (--code surface, 12px font-mono, success / destructive for changes); light and dark syntax colours follow the html.dark class. It loads on demand in the browser (about 200 KB gzipped, with grammars per language). During static export and until it arrives, plain unhighlighted rows show.
Motion
While status is streaming, the newest row slides in with a primary flash and the view follows the end. Under reduced motion rows appear without movement and the chevron stops rotating.
Accessibility
The header is a collapsible trigger and reads out "N additions, N deletions". The rows sit in a region labelled "Changes to {filename}"; the Unified / Split switch is a radio group.
API Reference
Built on the Collapsible primitive. Only the props this library adds or changes are listed; everything else passes through.
FileDiff
Disclosure for one file change with live +/− counts, rendering a unified or split diff. Also accepts every prop of Collapsible.Root.
- filename*
- string
- File path in the header; the directory part is dimmed. Also infers the language.
- diff= ""
- string
- Unified diff text for one file. Ignored when
lines,oldFile/newFileorfileDiffare given. - lines
- FileDiffLine[]
- Pre-parsed rows, e.g. appended one by one while an agent streams.
- oldFile
- string | null
- Old contents for a two-file diff (
nullwhen the file is new). Unchanged regions become expandable. - newFile
- string | null
- New contents for a two-file diff (
nullwhen the file was deleted). - fileDiff
- FileDiffMetadata
- An already parsed diff (from
parseDiffFromFile,parsePatchFilesordiffAcceptRejectHunk). Wins over every other source. - language
- string
- Syntax language id, inferred from
filenamewhen omitted. - status= "complete"
- "streaming" | "complete"
streamingshows a spinner, flashes new rows in and follows the end.- open
- boolean
- Controlled open state.
- defaultOpen= true
- boolean
- Initial open state when uncontrolled.
- onOpenChange
- (open: boolean) => void
- Called when the disclosure opens or closes.
- collapseOnComplete= false
- boolean
- Fold the diff away when
statusgoes fromstreamingtocomplete; it reopens when streaming starts again. - diffStyle
- "unified" | "split"
- Controlled layout.
- defaultDiffStyle= "unified"
- "unified" | "split"
- Initial layout when uncontrolled.
- onDiffStyleChange
- (style: "unified" | "split") => void
- Called when the header switch changes the layout.
- diffStyleToggle= false
- boolean
- Show the Unified / Split switch in the header.
- inlineChanges= true
- boolean | "word-alt" | "word" | "word-line" | "char"
- Highlight the changed part of each modified line.
trueis"word-alt";falseturns it off. - indicators= "bars"
- "bars" | "classic" | "none"
- Mark changed lines with a coloured bar, with
+/-signs, or by background only. - wrap= false
- boolean
- Wrap long lines instead of scrolling sideways.
- maxHeight= 320
- number
- Max height of the rows area in px.
- showLineNumbers= true
- boolean
- Show line numbers.
- copyable= true
- boolean
- Show a button that copies the new file contents.
- lineAnnotations
- DiffLineAnnotation<A>[]
- Rows of your own UI pinned under diff lines, each
{ side: "additions" | "deletions", lineNumber, metadata }. - renderAnnotation
- (annotation: DiffLineAnnotation<A>) => ReactNode
- Renders each annotation (comments, accept/reject buttons…).
- options
- FileDiffOptions
- Extra renderer options merged over the defaults, e.g.
hunkSeparators,expandUnchanged,enableLineSelection. - variant= "card"
- "card" | "row"
rowdrops the card surface and moves the chevron to the end, for lists of files such asEditedFilesCard.- actions
- ReactNode
- Extra header controls before the copy button (reveal in folder, open in editor).
| Prop | Type | Default | Description |
|---|---|---|---|
| filename* | string | — | File path in the header; the directory part is dimmed. Also infers the language. |
| diff | string | "" | Unified diff text for one file. Ignored when lines, oldFile/newFile or fileDiff are given. |
| lines | FileDiffLine[] | — | Pre-parsed rows, e.g. appended one by one while an agent streams. |
| oldFile | string | null | — | Old contents for a two-file diff (null when the file is new). Unchanged regions become expandable. |
| newFile | string | null | — | New contents for a two-file diff (null when the file was deleted). |
| fileDiff | FileDiffMetadata | — | An already parsed diff (from parseDiffFromFile, parsePatchFiles or diffAcceptRejectHunk). Wins over every other source. |
| language | string | — | Syntax language id, inferred from filename when omitted. |
| status | "streaming" | "complete" | "complete" | streaming shows a spinner, flashes new rows in and follows the end. |
| open | boolean | — | Controlled open state. |
| defaultOpen | boolean | true | Initial open state when uncontrolled. |
| onOpenChange | (open: boolean) => void | — | Called when the disclosure opens or closes. |
| collapseOnComplete | boolean | false | Fold the diff away when status goes from streaming to complete; it reopens when streaming starts again. |
| diffStyle | "unified" | "split" | — | Controlled layout. |
| defaultDiffStyle | "unified" | "split" | "unified" | Initial layout when uncontrolled. |
| onDiffStyleChange | (style: "unified" | "split") => void | — | Called when the header switch changes the layout. |
| diffStyleToggle | boolean | false | Show the Unified / Split switch in the header. |
| inlineChanges | boolean | "word-alt" | "word" | "word-line" | "char" | true | Highlight the changed part of each modified line. true is "word-alt"; false turns it off. |
| indicators | "bars" | "classic" | "none" | "bars" | Mark changed lines with a coloured bar, with +/- signs, or by background only. |
| wrap | boolean | false | Wrap long lines instead of scrolling sideways. |
| maxHeight | number | 320 | Max height of the rows area in px. |
| showLineNumbers | boolean | true | Show line numbers. |
| copyable | boolean | true | Show a button that copies the new file contents. |
| lineAnnotations | DiffLineAnnotation<A>[] | — | Rows of your own UI pinned under diff lines, each { side: "additions" | "deletions", lineNumber, metadata }. |
| renderAnnotation | (annotation: DiffLineAnnotation<A>) => ReactNode | — | Renders each annotation (comments, accept/reject buttons…). |
| options | FileDiffOptions | — | Extra renderer options merged over the defaults, e.g. hunkSeparators, expandUnchanged, enableLineSelection. |
| variant | "card" | "row" | "card" | row drops the card surface and moves the chevron to the end, for lists of files such as EditedFilesCard. |
| actions | ReactNode | — | Extra header controls before the copy button (reveal in folder, open in editor). |
- data-status
streamingorcomplete.- data-diff-style
unifiedorsplit.
| Data attribute | Description |
|---|---|
| data-status | streaming or complete. |
| data-diff-style | unified or split. |
parseUnifiedDiff
(diff: string) => FileDiffLine[]. Parses one file's git diff output; file headers are dropped.
FileDiffLine
One row of a diff.
- type*
- "added" | "removed" | "context" | "hunk"
- Row kind.
- content*
- string
- Line text without the leading sign; the full
@@header for hunks. - oldLine
- number
- Line number in the old file.
- newLine
- number
- Line number in the new file.
| Prop | Type | Default | Description |
|---|---|---|---|
| type* | "added" | "removed" | "context" | "hunk" | — | Row kind. |
| content* | string | — | Line text without the leading sign; the full @@ header for hunks. |
| oldLine | number | — | Line number in the old file. |
| newLine | number | — | Line number in the new file. |