Features
Version History
Vextro tracks versions of content documents. Editors can view what changed between saves, compare versions side-by-side, and restore previous versions. The diff system supports primitives, rich text (word-level), JSON (line-level), and arrays.
Diff strategies by field type
| Field Type | Diff Strategy | Granularity |
|---|---|---|
| Text, textarea, email, URL | Value comparison | Whole field |
| Rich text (TipTap JSON) | Word-level LCS | Words with formatting |
| JSON, code | Line-level LCS | Individual lines |
| Number, boolean, select | Value comparison | Whole field |
| Array, group | Deep equality | Per-element |
Document diff
import { diffDocuments } from "vextro/lib/versionDiff";
const diffs = diffDocuments({
oldDoc: previousVersion,
newDoc: currentVersion,
});
// { field: "title", type: "changed", oldValue: "Draft", newValue: "Final" }
// { field: "slug", type: "unchanged", oldValue: "my-post", newValue: "my-post" } Each diff entry has a type of added, removed, changed, or unchanged.
Rich text diff
Word-level diffing that preserves formatting marks (bold, italic, links):
import { diffRichText } from "vextro/lib/versionDiff";
const diff = diffRichText({
oldContent: prev.body,
newContent: curr.body,
});
// diff.segments:
// [
// { text: "The ", type: "unchanged" },
// { text: "quick", type: "removed", marks: [{ type: "bold" }] },
// { text: "fast", type: "added", marks: [{ type: "bold" }] },
// ] JSON and code diff
Line-level comparison using LCS:
import { diffJson } from "vextro/lib/versionDiff";
const diff = diffJson({ oldJson: prev.config, newJson: curr.config });
// diff.lines:
// [
// { text: ' "theme": "light"', type: "removed", lineNumber: 2 },
// { text: ' "theme": "dark"', type: "added", lineNumber: 3 },
// ] Restoring a version
Restoring creates a new save with the selected version's data. The version timeline is append-only -- no history is lost.
Filtering diffs
Pass a fields array to diffDocuments to limit comparison to specific fields. Internal fields prefixed with _ are excluded by default.