Skip to content
WingoUI
ComponentsTemplatesDocsPricingAI agentsBlogChangelogContact
Components
Buttons & Actions
ButtonButton GroupCopy ButtonDelete ButtonTheme ToggleToggleToggle Group
Inputs
Avatar UploadCalendarCheckboxColor PickerComboboxCountry SelectCron PickerCurrency InputDate PickerDate Range PickerDate Time PickerDuration PickerEmail InputEmoji PickerFile UploadFilter BuilderInputInput GroupListboxMasked InputMention InputMonth PickerMulti SelectNumber InputOTP InputPassword InputPhone InputRadio GroupRatingSearch InputSegmented ControlSelectSignature PadSliderSwitchTag InputTextareaTime PickerTimezone SelectTree Select
Forms
Address InputBooking WidgetCSV ImportFieldForm WizardInline EditInvoice line itemsRich Text EditorSchema Builder
Navigation
Back To TopBounce SidebarBreadcrumbCommand MenuGlobal SearchGooey NavHook SidebarNav MenuNav TabsPaginationProximity SidebarScroll ProgressSidebarStepsTable Of ContentsTabsTopbarUser MenuWorkspace Switcher
Overlays
Alert DialogContext MenuDialogDrawerDropdown MenuFamily DrawerHover CardPopoverShare DialogSheetShortcuts DialogTooltipTour
Feedback
AlertBannerCookie ConsentEmoji ReactionEmpty StateNotification BellNotification CenterProgressRoute ProgressSetup ChecklistSkeletonSpinnerToast
Data Display
Approval FlowAvatarBadgeCalendar ViewCode BlockComment ThreadContact cardCountdownDescription ListDiff ViewerFile ListFile managerFolderGanttJSON ViewerKbdLog ViewerMarkdownPresenceSchedulerTagTimelineTree View
Tables & Lists
Audit LogData TableDeal PipelineEditable GridKanbanListMembers tablePermissions MatrixTableTask ListVirtual List
Charts & Stats
Animated CounterArea chartBar chartDonut chartFunnel chartGaugeGitHub ActivityHeatmap calendarLeaderboardLine chartRadar chartRadial barSparklineStat CardTrackerTrend badge
Layout
AccordionApp ShellCardCollapsibleDetail LayoutEntity HeaderPage HeaderScroll AreaSeparatorSettings LayoutSplit ViewToolbar
Media
Audio PlayerCarouselCSV ViewerDOCX EditorDOCX ViewerExcel ViewerFile SystemImage compareImage CropperImage GalleryImage LightboxLocation MapPDF ViewerPowerPoint ViewerQR CodeStep PlayerVideo Player
AI Kit
Agent Status CardChat MessageChat ThreadChat WidgetConversation ListFluid OrbGrid RevealMatrix OrbModel PickerPrompt InputReasoning IndicatorRule BuilderSuggestion ChipsTool Call TimelineUsage MeterVoice InputWorkflow Canvas
Text & Effects
Background PatternGravity LettersMarqueeTerminalText AnimateText Rotate
Mobile
Action SheetBottom Tab BarFABHaptic ButtonHero HeaderInstall PromptLarge Title HeaderNotification BannerOnboarding CarouselPicker WheelPull To RefreshSlide To ConfirmSticky CTA BarSwipe DeckSwipeable Tabs
Commerce
Cart drawerCategory pickerCheckout stepsCoupon inputFavorite buttonFilter panelListing cardListing gridOrder trackerPricePrice rangePricing tableProduct galleryQuantity stepperReviewsSeller card
Marketing Sections
App Download SectionBlog Cards SectionCTA SectionFAQ SectionFeature GridFeature ShowcaseHero SectionIntegrations SectionLogo CloudMarketing FooterMarketing HeaderMarketing SectionNewsletter SectionPricing SectionStats SectionSteps SectionTeam SectionTestimonials SectionTimeline SectionVideo Section
Blocks
Admin Dashboard PageAdmin ShellAI Chat PageAPI Keys PageAutomation Builder PageCheckout PageCRM Contact PageCRM Pipeline PageError PagesInbox PageInvoice PageKPI DashboardListing Detail PageLogin PageMarketplace Home PageMarketplace Search PageMarketplace ShellMobile App HomeOnboarding FlowOrder History PagePost Listing WizardPricing PageProfile PageRegister PageSettings PageUsers Admin Page
Templates
About PageAcademy PageAccessibility PageAffiliate PageAgency PageAI Product PageAppointment Booking PageBlog PageBlog Post PageBug Bounty PageCareers PageChangelog PageCommunity PageCompare PageContact PageCustomer Story PageCustomers PageDemo Booking PageDevelopers PageDocs Home PageDownload PageEnterprise PageEvents PageExperts PageFAQ PageFeature PageGlossary PageHelp Article PageHelp Center PageHome Services PageImpact PageIntegration Detail PageIntegrations PageJob Detail PageLanding PageLaunch PageLegal PageManifesto PagePortfolio PagePress PageProduct Tour PagePromo PageReal Estate PageReferral PageResearch Report PageRestaurant PageRoadmap PageROI Calculator PageSecurity PageSolutions PageStartups PageStatus PageStorefront PageSwitch PageTemplate Gallery PageTravel PageWaitlist PageYear in Review Page
Hooks & Utilities
AI ChatChart kitCommerceDate utilsFile exportFile typesFiltersForm validationHapticsInvoicingMotion presetsNavigationNumber formatRelative timeRomanian locationsSpreadsheetText matchTonesUIProvideruseClipboarduseConsentuseControllableStateuseCooldownuseDebounceuseKeyboardInsetuseLocaleuseLongPressuseMediaControlsuseMediaQueryuseOverflowItemsusePersistedStateuseScrollEdgesuseScrollStateuseSelectionuseShortcutuseStickToBottom
GalleryTheme

Data Display

Diff Viewer

Data Display

Diff Viewer React component for Tailwind CSS v4

Description ListFile List
Live props
Content
oldValue
newValue
oldTitle
newTitle
State
defaultView
Appearance
granularity
lineNumbers
language
showStats
toolbar
size
radius
Layout
contextLines3 lines
wrap
maxHeight560px
Behavior
ignoreWhitespace
ignoreCase
Code
import { DiffViewer } from "@/components/ui/diff-viewer"

<DiffViewer />
  1. Home
  2. Components
  3. Data Display
  4. Diff Viewer
Pro

Diff Viewer React component for Tailwind CSS v4

v1.0.0·Added in 1.0.0

A React diff viewer for two texts: split or unified views, word highlights, collapsed context, syntax colors and jumps between changes.

Read the change at a glance: removed lines are tinted red with a minus, added lines green with a plus, and the words that changed inside a line are marked. Wide screens show the two versions side by side; phones (and the server render) show one unified column, and the switch in the toolbar, or the ⋯ menu on a phone (a bottom sheet), flips between them with a short crossfade. Long runs of unchanged lines fold into a row that says how many lines hide there: tap it to show them all, or use the arrows beside it to reveal 20 at a time; the new rows fade in. The chevron buttons, or Alt+Down and Alt+Up while focus is anywhere inside the viewer, scroll to the next or previous change, focus it and flash it briefly (no flash with reduced motion), wrapping around at the ends; a screen reader hears Change 2 of 5. The ⋯ menu copies either version and toggles line wrapping, ignoring whitespace and ignoring case; the copy buttons in the column headers do the same per side. Lines scroll sideways inside the viewer with the line numbers pinned, the body scrolls with the keyboard once focused, and every changed line carries a spoken added or removed label.

Diff Viewer is a Pro React component in the React data display components category, written in TypeScript for React 19 and Tailwind CSS v4. It installs as source with npx wingo-ui@latest add diff-viewer, together with Badge, Button, Copy Button, Dropdown Menu, Segmented Control and 6 more and the npm packages diff, prism-react-renderer, motion and lucide-react.

Add to a project

Requirements: React 19 and Tailwind CSS v4, in Next.js (App Router or Pages), Vite or React Router. Installation guide · Next.js setup · Vite setup

npx wingo-ui@latest add diff-viewer

Diff Viewer is part of Pro, with 298 more Pro items: $8 a month or $150 once. 14-day refund before you download any Pro item. See plans

Usage

Import DiffViewer from @/components/ui/diff-viewer.

import { DiffViewer } from "@/components/ui/diff-viewer"

const v3 = `Art. 5. Payment
The Client pays each invoice within 15 days of receiving it.
Payment is made by bank transfer.`

const v4 = `Art. 5. Payment
The Client pays each invoice within 30 calendar days of the issue date,
by bank transfer to account 4839 2017 5 at Chase.`

export function ContractChanges() {
  return (
    <DiffViewer
      oldValue={v3}
      newValue={v4}
      oldTitle="Agreement v3 · Sep 12, 2026"
      newTitle="Agreement v4 · Sep 24, 2026"
      labels={{ split: "Side by side", noChanges: "The versions match" }}
    />
  )
}

Props

  • oldValue*default "# Services agreement no. 118/2026 The parties: Acme Software Inc., Chicago, Tax ID 40-1122334, as the Provider, and Tech Solutions Inc., Tax ID 12-3456789, as the Client. ## Art. 4. Scope of the agreement The Provider licenses, sets up and maintains the Acme CRM platform for 25 users, as listed in Appendix 1. Services are delivered remotely, on business days, between 9:00 AM and 6:00 PM. ## Art. 5. Payment Monthly fee: $4,990 plus sales tax The Client pays each invoice within 15 days of receiving it. Payment is made by bank transfer or in cash at the Provider's office. Late payment accrues a penalty of 0.1% per day of the amount due, capped at the value of the overdue invoice. ## Art. 6. Term The agreement runs for 12 months, starting on the date it is signed. The agreement renews automatically for further 12-month periods unless either party terminates it in writing. Notice of termination is given at least 30 days before expiry. ## Art. 7. Confidentiality The parties keep confidential the information received while performing the agreement. The obligation stays in force for 2 years after the agreement ends. Public information and information required by a competent authority are not confidential. ## Art. 8. Force majeure Force majeure releases the party that invokes it from liability, as provided by law. The affected party notifies the other party within 5 days of the event. ## Art. 9. Disputes DISPUTES are settled amicably or, failing that, by the courts of Cook County, Illinois. ## Art. 10. Final provisions This agreement was signed in two copies, one for each party. "
    string

    The original text. Line endings are normalized (CRLF, LF), text with NUL bytes shows a binary notice, and inputs above 2,000 lines compare in idle time behind a skeleton.

  • newValue*default "# Services agreement no. 118/2026 The parties: Acme Software Inc., Chicago, Tax ID 40-1122334, as the Provider, and Tech Solutions Inc., Tax ID 12-3456789, as the Client. ## Art. 4. Scope of the agreement The Provider licenses, sets up and maintains the Acme CRM platform for 40 users, as listed in Appendix 1. Services are delivered remotely, on business days, between 9:00 AM and 6:00 PM. ## Art. 5. Payment Monthly fee: $6,490 plus sales tax The Client pays each invoice within 30 calendar days of the issue date, by bank transfer to account 4839 2017 5 (routing 021000021) at Chase. Late payment accrues a penalty of 0.1% per day of the amount due, capped at the value of the overdue invoice. ## Art. 6. Term The agreement runs for 12 months, starting on the date it is signed. The agreement renews automatically for further 12-month periods unless either party terminates it in writing. Notice of termination is given at least 30 days before expiry. ## Art. 7. Confidentiality The parties keep confidential the information received while performing the agreement. The obligation stays in force for 2 years after the agreement ends. Public information and information required by a competent authority are not confidential. ## Art. 8. Force majeure Force majeure releases the party that invokes it from liability, as provided by law. The affected party notifies the other party within 5 days of the event. ## Art. 9. Disputes Disputes are settled amicably or, failing that, by the courts of Cook County, Illinois. ## Art. 10. Final provisions This agreement was signed in two copies, one for each party. Appendices 1 and 2 are an integral part of the agreement. "
    string

    The changed text.

  • oldTitledefault "Agreement v3 · Sep 12, 2026"
    ReactNode

    The original's name above its column (split) or first in the header (unified). Empty shows labels.original ("Original").

  • newTitledefault "Agreement v4 · Sep 24, 2026"
    ReactNode

    The changed version's name. Empty shows labels.modified ("Changed").

  • defaultViewdefault "auto"
    "auto" | "split" | "unified"

    The starting view. auto is unified below 768px and on the server, split from 768px. Use view and onViewChange to control it.

  • view
    "auto" | "split" | "unified"

    Controlled view.

  • onViewChange
    (view: "split" | "unified") => void

    Called when the person switches the view in the toolbar or the ⋯ menu.

  • granularitydefault "word"
    "word" | "char" | "none"

    Highlights inside a changed line pair: whole words, single characters, or none. Pairs that share 40% of their text or less get the line tint only.

  • contextLinesdefault 3
    number

    Unchanged lines kept around each change; longer unchanged runs fold into an expander row. -1 shows every line.

  • lineNumbersdefault true
    boolean

    The old and new line number gutters. They stay pinned while long lines scroll sideways.

  • wrapdefault false
    boolean

    Long lines wrap instead of scrolling sideways. The ⋯ menu toggles it too; a new prop value resets it.

  • ignoreWhitespacedefault false
    boolean

    Lines that differ only in spaces or tabs count as unchanged (and a missing final newline is ignored). Also in the ⋯ menu.

  • ignoreCasedefault false
    boolean

    Lines that differ only in upper and lower case count as unchanged. Also in the ⋯ menu.

  • languagedefault ""
    string

    A Prism language for syntax colors (tsx, jsx, ts, js, json, yaml, markdown, css, sql, python, go...). Colors come from the tone tokens, so both themes read well. Empty keeps plain text.

  • showStatsdefault true
    boolean

    The +12 −3 badges in the toolbar (spoken as 12 additions, 3 deletions).

  • toolbardefault true
    boolean

    The toolbar with the stats, the view switch, previous / next change and the ⋯ menu. Alt+Up / Alt+Down keep working without it.

  • sizedefault "md"
    "sm" | "md"

    Mono text 12px on 20px lines, or 13px on 22px lines.

  • radiusdefault "md"
    "none" | "sm" | "md" | "lg" | "full"

    Corner radius of the panel: 0, 12, 16 or 24px (full falls back to lg).

  • maxHeightdefault 560
    number

    Height cap in px; the body scrolls inside it with a sticky header. 0 lets the viewer grow with its content.

  • onLineClick
    (line: DiffLine, side: "old" | "new") => void

    Turns the line numbers into buttons, e.g. to comment on a line. DiffLine is { type: "added" | "removed" | "unchanged" | "collapsed"; oldNumber?; newNumber?; content; parts?; hidden?; noNewline? }. Unified rows with both numbers report the side clicked.

  • renderGutter
    (line: DiffLine, side: "old" | "new") => ReactNode

    An extra cell in each gutter, after the numbers: comment counts, blame avatars, coverage marks.

  • setApi
    (api: DiffViewerApi) => void

    Receives { nextChange(), previousChange(), expandAll(), collapseUnchanged() } once after mount; the object keeps its identity.

  • labels
    Partial<DiffViewerLabels>

    Every visible and spoken string, English by default (DEFAULT_DIFF_VIEWER_LABELS): split, unified, view, nextChange, previousChange, more, cancel, expand(count), expandUp(count), expandDown(count), added, removed, noChanges, copyOld, copyNew, copied, copyFailed, wrap, ignoreWhitespace, ignoreCase, stats(stats), changes(count), changeOf(index, total), caption(old, new, stats), original, modified, computing, noNewline, lineEndingsOnly, binary, line(n).

  • classNames
    Partial<Record<"toolbar" | "header" | "title" | "body" | "table" | "row" | "number" | "sign" | "content" | "word" | "collapsed" | "empty", string>>

    Extra classes for the inner parts.

  • className
    string

    Extra classes for the panel, merged last.

Parts

  • computeDiff

    function

    The framework-free diff the viewer uses: { rows: DiffRow[], stats: { added, removed, changed }, blocks, lineEndingsOnly, binary }. Rows are unchanged pairs, change pairs (removed left, added right, with word parts) and collapsed runs. Signature: (oldValue: string, newValue: string, options?: { granularity?, ignoreWhitespace?, ignoreCase?, contextLines? }) => DiffResult.

  • DEFAULT_DIFF_VIEWER_LABELS

    constant

    Every English string: the Split / Unified switch, Next and Previous change, the expand buttons ("Show 12 unchanged lines"), copy and option menu items, stats ("3 additions, 1 deletion"), changeOf and the screen reader caption. Spread it into labels to translate a few. Type: DiffViewerLabels.

  • DiffLine

    type

    One line as onLineClick receives it: { type: "added" | "removed" | "unchanged" | "collapsed", oldNumber?, newNumber?, content, parts?, hidden?, noNewline? }.

Examples

Code review

A tsx change with syntax colors; unchanged runs fold around three lines of context.

Config with ignoreWhitespace

Only the real edits count: trailing spaces and double spaces are ignored.

3,000 lines, one change

Big inputs compare in idle time behind a skeleton, then fold down to the one change.

Character highlights

Unified with granularity char, for short edits like a product title.

Line comments

renderGutter shows comment counts; onLineClick opens a comment box.

No differences

Identical texts show a calm empty state; the file still unfolds on demand.

Dependencies

diffprism-react-renderermotionlucide-reactbadgebuttoncopy-buttondropdown-menusegmented-controltooltipuse-clipboarduse-controllable-stateuse-media-querymotionui-config

Files

  • components/ui/diff-viewer.tsx

FAQ

Is the React Diff Viewer component free?

The preview, props and docs are free; copying the source needs a Pro plan. See plans.

How do I install Diff Viewer?

Run npx wingo-ui@latest add diff-viewer. It also adds the Wingo UI items it builds on (Badge, Button, Copy Button, Dropdown Menu, Segmented Control and 6 more) and the npm packages diff, prism-react-renderer, motion and lucide-react. With shadcn, add the @wingo-ui registry with your API key, then run npx shadcn@latest add @wingo-ui/diff-viewer.

How does Diff Viewer behave on phones?

Wide screens show the two versions side by side; phones (and the server render) show one unified column, and the switch in the toolbar, or the ⋯ menu on a phone (a bottom sheet), flips between them with a short crossfade.

See also

Code BlockJSON ViewerAudit LogCopy ButtonSegmented Control

Used in

Schema Builder

More React data display components

File ListFolderGanttLog ViewerMarkdownPresence
All 23 React data display components

Articles using this component

  • Audit Log UI in React: Filters, Diffs and CSV Export

    Component guides · Nov 6, 2026 · 10 min read

  • React Diff Viewer in React 19: Split, Unified, Word Diffs

    Component guides · Oct 16, 2026 · 9 min read

PreviousDescription ListNext File List
+7−65 changes
Differences between Agreement v3 · Sep 12, 2026 and Agreement v4 · Sep 24, 2026: 7 additions, 6 deletions
−Agreement v3 · Sep 12, 2026→+Agreement v4 · Sep 24, 2026
44​
55## Art. 4. Scope of the agreement
66The Provider licenses, sets up and maintains the Acme CRM platform
7−removed: for 25 users, as listed in Appendix 1.
7+added: for 40 users, as listed in Appendix 1.
88Services are delivered remotely, on business days, between 9:00 AM and 6:00 PM.
99​
1010## Art. 5. Payment
11−removed: Monthly fee: $4,990 plus sales tax
12−removed: The Client pays each invoice within 15 days of receiving it.
13−removed: Payment is made by bank transfer or in cash at the Provider's office.
11+added: Monthly fee: $6,490 plus sales tax
12+added: The Client pays each invoice within 30 calendar days of the issue date,
13+added: by bank transfer to account 4839 2017 5 (routing 021000021) at Chase.
1414Late payment accrues a penalty of 0.1% per day of the amount due, capped at the value of the overdue invoice.
1515​
1616## Art. 6. Term
2323The obligation stays in force for 2 years after the agreement ends.
2424Public information and information required by a competent authority are not confidential.
2525​
26−removed: ## Art. 8. Force majeure
26+added: ## Art. 8. Force majeure
2727Force majeure releases the party that invokes it from liability, as provided by law.
2828The affected party notifies the other party within 5 days of the event.
2929​
3030## Art. 9. Disputes
31−removed: DISPUTES are settled amicably or, failing that, by the courts of Cook County, Illinois.
31+added: Disputes are settled amicably or, failing that, by the courts of Cook County, Illinois.
3232​
3333## Art. 10. Final provisions
3434This agreement was signed in two copies, one for each party.
35+added: Appendices 1 and 2 are an integral part of the agreement.

defaultView="auto" · Alt+↓ / Alt+↑ jump between changes