A set of modern, intuitive, and accessible React components to render differences between two strings. Built with TypeScript, compatible with React 16.8 through React 19+, and supporting ESM and CommonJS.
- ⚡ Zero-config & Lightweight: Works immediately upon install with subtle, polished default styling.
- ♿ Built-in Accessibility (a11y): Uses semantic
<ins>and<del>elements with screen-reader announcements by default. - 🎨 Flexible Theming: Supports CSS Custom Properties (CSS variables) for effortless dark mode and custom palette integration.
- 🛠️ Granular Customization: Custom render functions (
renderAdded,renderRemoved,renderPart) and headless hooks (useDiffWords,useDiffChars, etc.). - 📦 Modern Dual Package: Shipped with complete TypeScript declaration files (
.d.ts), sourcemaps, ESM (.mjs), and CJS (.cjs). - 🔄 Broad React Support: Compatible with React 16.8, 17, 18, and 19.
npm install react-diff-components
# or
yarn add react-diff-components
# or
pnpm add react-diff-componentsimport React from 'react';
import { DiffChars, DiffWords, DiffLines, DiffSentences } from 'react-diff-components';
export function Example() {
return (
<div>
{/* Word diff */}
<DiffWords
from="The quick brown fox jumps"
to="The fast brown dog jumps"
/>
{/* Character diff */}
<DiffChars
from="https://example.com/a"
to="https://example.com/A"
ignoreCase
/>
{/* Sentence diff */}
<DiffSentences
from="Sentence one. Sentence two."
to="Sentence one. Sentence three."
/>
{/* Multi-line code diff */}
<DiffLines
from={`const x = 1;\nconst y = 2;`}
to={`const x = 1;\nconst y = 3;`}
/>
</div>
);
}All components inherit from DiffBaseProps:
| Prop | Type | Default | Description |
|---|---|---|---|
from |
string |
(Required) | The original text before changes. |
to |
string |
(Required) | The modified text after changes. |
hideAdded |
boolean |
false |
If true, hides added text. |
hideRemoved |
boolean |
false |
If true, hides removed text. |
hideEqual |
boolean |
false |
If true, hides unchanged text. |
className |
string |
undefined |
CSS class name applied to the root element. |
style |
React.CSSProperties |
undefined |
Inline styles applied to the root element. |
classNames |
DiffClassNames |
undefined |
Granular classes: { root?, added?, removed?, equal? }. |
styles |
DiffStyles |
undefined |
Granular styles: { root?, added?, removed?, equal? }. |
unstyled |
boolean |
false |
If true, skips automatic style injection. |
srLabelAdded |
string | false |
" [added: ]" |
Screen reader label for additions (false to disable). |
srLabelRemoved |
string | false |
" [removed: ]" |
Screen reader label for removals (false to disable). |
renderAdded |
(value, part) => ReactNode |
undefined |
Custom renderer function for additions. |
renderRemoved |
(value, part) => ReactNode |
undefined |
Custom renderer function for removals. |
renderEqual |
(value, part) => ReactNode |
undefined |
Custom renderer function for unchanged parts. |
renderPart |
(part, defaultNode) => ReactNode |
undefined |
Unified custom renderer for all parts. |
children |
(parts: DiffPart[]) => ReactNode |
undefined |
Children as a render prop for headless custom rendering. |
<DiffChars />&<DiffWords />:ignoreCase?: boolean(default:false): Perform case-insensitive diffing.
<DiffWords />:ignoreSpaces?: boolean(default:false): Disregard whitespace differences between words.
By default, styles are automatically and safely injected into the document head on the client side with zero setup required.
Customize colors and typography easily using CSS variables:
:root {
--rdc-bg: transparent;
--rdc-font-size: 1em;
--rdc-font-family: monospace;
--rdc-line-height: 1.4em;
--rdc-added-color: #285000;
--rdc-added-bg: rgba(40, 100, 0, 0.12);
--rdc-removed-color: #c81414;
--rdc-removed-bg: rgba(200, 0, 0, 0.12);
--rdc-equal-color: inherit;
}
/* Dark Mode Example */
[data-theme='dark'] {
--rdc-added-color: #4ade80;
--rdc-added-bg: rgba(74, 222, 128, 0.2);
--rdc-removed-color: #f87171;
--rdc-removed-bg: rgba(248, 113, 113, 0.2);
--rdc-equal-color: #94a3b8;
}If you prefer static CSS imports or are disabling auto-injection via unstyled:
import 'react-diff-components/style.css';Supply custom functions to render semantic tags, tooltips, or custom badges:
<DiffWords
from="Old status: Pending"
to="New status: Approved"
renderAdded={(val) => <span className="badge badge-success">+{val}</span>}
renderRemoved={(val) => <span className="badge badge-danger">-{val}</span>}
/>For advanced use cases (e.g. split diff views, side-by-side tables), use the exported hooks directly:
import { useDiffWords } from 'react-diff-components';
function SplitDiff({ from, to }: { from: string; to: string }) {
const parts = useDiffWords(from, to);
return (
<div>
{parts.map((part) => (
<span
key={part.key}
style={{ color: part.added ? 'green' : part.removed ? 'red' : 'inherit' }}
>
{part.value}
</span>
))}
</div>
);
}Available hooks:
useDiffChars(from, to, options?)useDiffWords(from, to, options?)useDiffLines(from, to)useDiffSentences(from, to)