text-visual-diff
v0.2.0
Published
Compare HTML source against plain text and highlight the differences
Maintainers
Readme
text-visual-diff
Compare HTML source against plain text and highlight the differences.
text-visual-diff extracts visible text from an HTML string (decoding entities along the way), diffs it word-by-word against a target plain-text string, and returns highlighted HTML for both sides — ready to drop into a browser, or consume as structured data.
Install
npm install text-visual-diffQuick start
import { diffText } from "text-visual-diff";
const result = diffText(
"<p>Hello <b>world</b></p>",
"Hello earth"
);
console.log(result.highlightedSource);
// <mark class="diff-matched">Hello </mark><b><mark class="diff-unmatched">world</mark></b>
console.log(result.highlightedTarget);
// <mark class="diff-matched">Hello </mark><mark class="diff-added">earth</mark>
console.log(result.extractedText);
// "Hello world"
console.log(result.segments);
// [
// { type: "matched", text: "Hello ", sourceStart: 3, sourceEnd: 9 },
// { type: "unmatched", text: "world", sourceStart: 12, sourceEnd: 17 }
// ]API
diffText(sourceHtml, targetText, options?)
Main entry point. Strips tags from sourceHtml, decodes HTML entities, diffs the extracted text against targetText, and returns a DiffTextResult.
diffPlainText(sourceText, targetText, options?)
Compare two plain-text strings and highlight the differences. Unlike diffText, no HTML tags are stripped and no entities are decoded — both inputs are treated as literal plain text.
DiffTextResult
| Field | Type | Description |
| ------------------- | ------------------- | ------------------------------------------------------------------ |
| highlightedSource | string | Source HTML with highlight tags wrapped around matched/unmatched text |
| highlightedTarget | string | Target plain text with highlight tags wrapped around matched/added text |
| extractedText | string | Plain text extracted from the source HTML (entities decoded) |
| targetText | string | The targetText argument, included for convenience |
| segments | HighlightSegment[]| Structured segments describing each highlighted region in the source |
| similarity | number | Dice similarity coefficient between 0 and 1 |
HighlightOptions
All options default to "mark" for tags and "diff-matched" / "diff-unmatched" / "diff-added" for classes.
| Option | Default | Description |
| ---------------- | ------------------ | ----------------------------------- |
| matchedTag | "mark" | HTML tag for matched regions |
| matchedClass | "diff-matched" | CSS class for matched regions |
| unmatchedTag | "mark" | HTML tag for unmatched (removed) regions |
| unmatchedClass | "diff-unmatched" | CSS class for unmatched regions |
| addedTag | "mark" | HTML tag for added (target-only) regions |
| addedClass | "diff-added" | CSS class for added regions |
| showWhitespaceDiffs | false | When true, highlights whitespace-only differences |
HighlightSegment
| Field | Type | Description |
| ------------- | --------------------------------- | ---------------------------------------------------------------- |
| type | "matched" \| "unmatched" \| "added" | Whether this region matched, was removed, or was added |
| text | string | The visible text content inside this region |
| sourceStart | number | Start offset (inclusive) within the original sourceHtml |
| sourceEnd | number | End offset (exclusive) within the original sourceHtml |
Integrations
React
import { useDiffText } from "text-visual-diff/react";
function DiffView({ html, text }) {
const result = useDiffText(html, text);
return <div dangerouslySetInnerHTML={{ __html: result.highlightedSource }} />;
}Stimulus
<div data-controller="text-visual-diff"
data-text-visual-diff-source-html-value="<p>Hello</p>"
data-text-visual-diff-target-text-value="Hello">
</div>import { DiffTextController } from "text-visual-diff/stimulus";
application.register("text-visual-diff", DiffTextController);CDN / IIFE
<script src="https://cdn.jsdelivr.net/npm/text-visual-diff/dist/text-visual-diff.iife.js"></script>
<script>
const result = TextVisualDiff.diffText("<p>Hello</p>", "Hello");
</script>Themes
Two built-in CSS themes are available:
/* Default — green matched, red unmatched, blue added */
@import "text-visual-diff/theme/default";
/* Subtle — lighter tones with dashed underlines */
@import "text-visual-diff/theme/subtle";You can also write your own — the default class names are diff-matched, diff-unmatched, and diff-added.
License
MIT
