Visual primitive

Text diff

<DomTextDiff>

A standalone text comparison for AI rewrites, code patches, editorial review, and before-and-after copy changes.

Playground

Compare text live

Text diff playground

Switch between the complete diff, current text, and finished replacement. Inline, unified, and split still control how the diff itself is arranged.

CurrentSuggested
−6+6
WriteCreate betterclearer pages with aan flexibleeditor visualbuilt editorfor focused teams.
Playground.vuevue
vue
<script setup>
import { reactive } from 'vue';
import { DomTextDiff } from '@getdom/studio/vue';

const data = reactive({
	  "original": "Write better pages with a flexible visual editor.",
	  "proposed": "Create clearer pages with an editor built for focused teams.",
	  "mode": "diff",
	  "view": "inline",
	  "format": "text",
	  "originalLabel": "Current",
	  "proposedLabel": "Suggested",
	  "showHeader": true,
	  "showModeSwitcher": true,
	  "framed": true
	});
</script>

<template>
	<DomTextDiff
		v-bind="data"
	/>
</template>

Code

Cursor-style file edit

Use the unified code view for a compact patch with line numbers and word-level emphasis inside changed lines.

app/metadata.js

AI edit
Current fileProposed edit
−2+5
1 1export async function generateMetadata(page) {
2 const description = page.seoDescription || page.description;
3
2 4 return {
3 title: page.title,
4 description: page.description,
5 title: page.seoTitle || page.title,
6 description,
7 robots: page.noIndex ? 'noindex' : 'index',
5 8 };
6 9}
vue
<script setup>
import { DomBadge, DomTextDiff } from '../../../lib/vue';

const original = `export async function generateMetadata(page) {
	return {
		title: page.title,
		description: page.description,
	};
}`;

const proposed = `export async function generateMetadata(page) {
	const description = page.seoDescription || page.description;

	return {
		title: page.seoTitle || page.title,
		description,
		robots: page.noIndex ? 'noindex' : 'index',
	};
}`;
</script>

<template>
	<div class="w-full max-w-3xl overflow-hidden rounded-xl border border-border bg-canvas shadow-lg">
		<header class="flex items-center justify-between gap-3 border-b border-border bg-secondary/35 px-4 py-2.5">
			<div class="flex min-w-0 items-center gap-2">
				<svg class="size-4 shrink-0 text-muted-fg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true">
					<path d="M6 3h9l4 4v14H6V3Zm9 0v5h4M9 12h6M9 16h6" />
				</svg>
				<p class="truncate font-mono text-xs font-medium text-canvas-fg">app/metadata.js</p>
			</div>
			<DomBadge label="AI edit" tone="primary" size="sm" />
		</header>

		<DomTextDiff
			:original="original"
			:proposed="proposed"
			view="unified"
			format="code"
			original-label="Current file"
			proposed-label="Proposed edit"
			:framed="false"
		/>
	</div>
</template>

Writing

Word processor suggestion

Keep review actions app-owned while DomTextDiff renders the semantic insertion and deletion markup.

Launch announcement

Draft · Product update

AI suggestion

A clearer way to publish

The opening paragraph has a suggestion ready for review.

Current paragraphSuggested rewrite
−9+8
Our platform gives teams manyGive toolsevery thatteam canone befocused usedplace to create, review, and publish content in a number of differentwith waysconfidence.

Review the wording before changing the document.

vue
<script setup>
import { ref } from 'vue';
import { DomBadge, DomButton, DomTextDiff } from '../../../lib/vue';

const current = 'Our platform gives teams many tools that can be used to create, review, and publish content in a number of different ways.';
const suggested = 'Give every team one focused place to create, review, and publish content with confidence.';
const decision = ref('');

/**
 * Records the reviewer's decision for the suggestion demo.
 *
 * @param {'accepted'|'rejected'} value Review decision.
 * @returns {void}
 */
function decide(value) {
	decision.value = value;
}
</script>

<template>
	<article class="w-full max-w-2xl rounded-xl border border-border bg-canvas shadow-lg">
		<header class="flex flex-wrap items-center justify-between gap-3 border-b border-border px-5 py-3">
			<div>
				<p class="text-sm font-semibold text-canvas-fg">Launch announcement</p>
				<p class="mt-0.5 text-xs text-muted-fg">Draft · Product update</p>
			</div>
			<DomBadge label="AI suggestion" tone="primary" size="sm" />
		</header>

		<div class="space-y-4 p-5">
			<div class="border-b border-border pb-4 text-[15px] leading-7 text-canvas-fg">
				<p class="font-serif text-2xl font-semibold tracking-tight">A clearer way to publish</p>
				<p class="mt-3 text-muted-fg">The opening paragraph has a suggestion ready for review.</p>
			</div>

			<DomTextDiff
				:original="current"
				:proposed="suggested"
				view="inline"
				original-label="Current paragraph"
				proposed-label="Suggested rewrite"
			/>

			<footer class="flex items-center justify-between gap-3">
				<p class="text-xs text-muted-fg">
					{{ decision ? `Suggestion ${decision}` : 'Review the wording before changing the document.' }}
				</p>
				<div class="flex items-center gap-2">
					<DomButton size="sm" variant="secondary" @click="decide('rejected')">Reject</DomButton>
					<DomButton size="sm" variant="primary" @click="decide('accepted')">Accept</DomButton>
				</div>
			</footer>
		</div>
	</article>
</template>

SEO

Meta description rewrite

Compare the current metadata value with an AI replacement without embedding acceptance or persistence rules in the diff primitive.

Meta description

AI found a clearer description for the search result.

SEO suggestion
Current descriptionSuggested replacement
−14+15
DOMBuild Studiopolished isVue aapplication collectioninterfaces ofwith Vueeditable components, thatpractical helpsblocks, developersand buildsource websitesyour andteam appscan own.

115 / 160 characters

vue
<script setup>
import { DomButton, DomTextDiff } from '../../../lib/vue';

const currentDescription = 'DOM Studio is a collection of Vue components that helps developers build websites and apps.';
const suggestedDescription = 'Build polished Vue application interfaces with editable components, practical blocks, and source your team can own.';
</script>

<template>
	<div class="w-full max-w-2xl rounded-xl border border-border bg-canvas p-5 shadow-sm">
		<div class="flex flex-wrap items-start justify-between gap-3">
			<div>
				<p class="text-sm font-semibold text-canvas-fg">Meta description</p>
				<p class="mt-1 text-xs leading-5 text-muted-fg">AI found a clearer description for the search result.</p>
			</div>
			<span class="rounded-full bg-primary/10 px-2.5 py-1 text-xs font-medium text-primary">SEO suggestion</span>
		</div>

		<DomTextDiff
			class="mt-4"
			:original="currentDescription"
			:proposed="suggestedDescription"
			view="inline"
			original-label="Current description"
			proposed-label="Suggested replacement"
		/>

		<div class="mt-4 flex flex-wrap items-center justify-between gap-3">
			<p class="text-xs text-muted-fg">{{ suggestedDescription.length }} / 160 characters</p>
			<div class="flex items-center gap-2">
				<DomButton size="sm" variant="secondary">Keep current</DomButton>
				<DomButton size="sm" variant="primary">Use suggestion</DomButton>
			</div>
		</div>
	</div>
</template>

Reference

Props

Control props

NameTypeTSDefaultDescription
originalstringstring''Current or original text.
proposedstringstring''Suggested replacement text.
mode'diff' | 'original' | 'replacement'string'diff'Show the complete diff, current text, or finished replacement.
view'inline' | 'unified' | 'split'string'inline'Inline prose, unified patch, or side-by-side comparison.
format'text' | 'code'string'text'Use proportional text or code-oriented monospace styling.
originalLabelstringstring'Original'Label for the original content.
proposedLabelstringstring'Suggested'Label for the proposed content.
showHeaderbooleanbooleantrueShow comparison labels and change counts.
showModeSwitcherbooleanbooleantrueShow the Diff, Current, and Replacement controls inside the standard header.
framedbooleanbooleantrueRender the standard border, radius, and canvas background.

Auto-generated from Text diff.props and inline _edit hints.

Events

NamePayloadDescription
@update:mode'diff' | 'original' | 'replacement'Fired when the active content mode changes.
@mode-change'diff' | 'original' | 'replacement'Fired after the viewer switches between the diff, current text, and replacement text.

Names auto-detected from defineEmits and source emit() calls; payload and description from __doc.events when present.

Slots

NameScopeDescription
#header{ additions, removals, mode, setMode, originalLabel, proposedLabel }Replace the comparison header and optionally control the active content mode.