Skip to content

Diff Viewer

Show the difference between two versions of a file, unified or side by side, with what changed inside each line marked.

import { DiffViewer } from '@youj/react'

Installation & usage

npm i @youj/react

For an app already set up with npx @youj/cli init, which installs the peers (@youj/core, @youj/motion, motion and Base UI) and wires the provider. New app? Start at Installation.

Use a diff viewer wherever someone reviews a change to code or config: a pull request, a deploy's changes, a config history, an AI edit waiting for approval. For a snippet that isn't a change, use Code Block.

import { DiffViewer } from '@youj/react'

export function ConfigChange({ previous, current }: { previous: string; current: string }) {
  return <DiffViewer before={previous} after={current} filename="config/app.json" language="json" />
}
  • Two versions or a patch. before and after are compared line by line. Line endings don't count (CRLF reads as LF), but a missing newline at the end of the file does, as in git. Or pass patch, a unified diff; only its first file is shown, and the lines between its hunks can't be opened because the patch doesn't carry them.
  • Highlighting is CodeBlock's built-in tokenizer, so language takes the same values (ts, tsx, json, yaml, css, py, go…). It produces tokens, never HTML.
  • Unchanged lines fold beyond context lines on each side of a change (3 by default) into a row that opens in place. context={Infinity} shows every line.
  • View and wrap are view/defaultView/onViewChange and wrap/defaultWrap/onWrapChange, so an app can remember them.
  • Copy copies after, or the patch when you passed one.

Reference

Propsbefore, after, patch, language, filename, view, wrap, context…

Props

DiffViewer props
PropType
beforestring

The old version of the file. With after; leave both out to pass a patch.

–
afterstring

The new version of the file.

–
patchstring

A unified diff (git diff output) to show instead of before/after. Only its first file is shown, and the lines between hunks can't be opened.

–
languagestring

Language for highlighting, as CodeBlock takes it: ts, tsx, json, css, py, go, yaml… Anything else is plain text.

default ''

''
filenamestring

The file's path for the header. A patch names its own when this is left out.

–
viewDiffView
'unified''split'

unified (one column, removals over additions) or split (before and after side by side), controlled.

–
defaultViewDiffView
'unified''split'

The view at first, uncontrolled.

default 'unified'

'unified'
onViewChange(view: DiffView) => void–
wrapboolean

Wrap long lines instead of scrolling sideways, controlled. The split view always wraps.

–
defaultWrapboolean

Wrap at first, uncontrolled.

default false

false
onWrapChange(wrap: boolean) => void–
contextnumber

Unchanged lines kept on each side of a change; longer runs fold. Infinity shows every line.

default 3

3
soundboolean

With sound on (YoujProvider’s sound), the view switch, wrap toggle and copy sound as the rest of the kit does. false keeps it silent.

default true

true
Other props pass through: extends ComponentProps<'figure'>.

Data attributes

The root carries data-yj-view (unified or split) and data-yj-wrap when lines wrap. Each line's code cell carries data-yj-line (add, del or context), and the row of the change you stepped to carries data-yj-current.

MotionStepping, view switch, copy · reduced motion

Motion

Motion tokens
MomentTokens
Step to a changethe change glides to the middle of the view and an accent bar marks its first line
no token

smooth scroll

smooth scroll
View switchthe segmented control’s indicator glides to the pressed view
spring.smooth

0.36s, bounce 0

0.36s, bounce 0
Copiedthe copy icon’s check, then back to rest after a beat
no token

3 × deliberate

3 × deliberate

Reduced motion

Stepping jumps to the change, the view indicator moves at once and the copy icon swaps to its check.

Reduced motion

Every state shows at once; the diff reads the same.

AccessibilityTable, keyboard, announcements

Accessibility

  • The viewer is a figure named by the file name, and the diff is a table whose caption names the file and counts the additions and deletions.
  • Each changed line reads its change and number first: "Added line 12: …", "Removed line 9: …". Line numbers and the +/− signs are decorative and can't be selected, so copying by hand takes only the code. In the split view, the columns are headed Before and After.
  • The diff is a focusable region. J and K, or Alt+↓ and Alt+↑, step to the next and previous change (described on the region, and as aria-keyshortcuts); the header's position, "Change 3 of 7", is announced politely. Previous and Next change are buttons too.
  • A folded run is a button, "12 unchanged lines"; opening it keeps focus in the diff.
  • The view is a segmented control and wrap a toggle button, each named. Copy announces its result, and a refused clipboard says so.
  • On a right-to-left page the header follows the page while the code stays left to right, as code does.