Skip to content
Data Display

DiffViewer

A diff, unified or side by side, with the syntax colouring the rest of the library uses.

Walkthrough

1. Basic

Give it before and after. A second diff runs inside a changed pair of lines, so a renamed variable shows as one word moving rather than two whole lines replaced.

total.ts +3 −3
export function total(items) {
export function total(items, tax = 0) {
let sum = 0;
for (const item of items) {
sum += item.price;
sum += item.price * item.qty;
}
return sum;
return sum * (1 + tax);
}
<script lang="ts">	import { DiffViewer } from 'omaris';​	const before = `export function total(items) {	let sum = 0;	for (const item of items) {		sum += item.price;	}	return sum;}`;​	const after = `export function total(items, tax = 0) {	let sum = 0;	for (const item of items) {		sum += item.price * item.qty;	}	return sum * (1 + tax);}`;</script>​<DiffViewer {before} {after} lang="ts" title="total.ts" class="w-full" />

2. Split and context

switchable offers the unified / split toggle, and view is bindable so the choice is yours to keep. context is how many unchanged lines survive either side of a change — the rest collapse into a strip you can click open.

tokens.ts (main) tokens.ts (branch) +3 −2
// boilerplate line 4
// boilerplate line 4
const theme = 'light';
const theme = 'system';
const radius = 8;
const radius = 12;
const easing = 'emphasized';
// trailing line 1
// trailing line 1
// trailing line 2
// trailing line 2
// trailing line 3
// trailing line 3
// trailing line 4
// trailing line 4
view: split
<script lang="ts">	import { DiffViewer, Text, type DiffView } from 'omaris';​	const before = `// boilerplate line 1// boilerplate line 2// boilerplate line 3// boilerplate line 4const theme = 'light';const radius = 8;// trailing line 1// trailing line 2// trailing line 3// trailing line 4`;​	const after = `// boilerplate line 1// boilerplate line 2// boilerplate line 3// boilerplate line 4const theme = 'system';const radius = 12;const easing = 'emphasized';// trailing line 1// trailing line 2// trailing line 3// trailing line 4`;​	let view = $state<DiffView>('split');</script>​<div class="flex w-full flex-col gap-2">	<DiffViewer		{before}		{after}		lang="ts"		bind:view		switchable		context={1}		titles={['tokens.ts (main)', 'tokens.ts (branch)']}		height="20rem"	/>	<Text variant="label-sm" tone="muted">view: {view}</Text></div>

3. From a patch

Hand it the patch that git diff printed and it renders that instead — identically, since both end up in the same shape. wrap keeps long lines on screen, and classes.viewport replaces the height the property sets.

+3 −1
import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';
import { extendTailwindMerge } from 'tailwind-merge';
const twMerge = extendTailwindMerge({ extend: { classGroups: { shadow: ['shadow-1'] } } });
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
<script lang="ts">	import { DiffViewer } from 'omaris';​	const patch = `diff --git a/src/lib/utils.ts b/src/lib/utils.ts--- a/src/lib/utils.ts+++ b/src/lib/utils.ts@@ -1,7 +1,8 @@ import { clsx, type ClassValue } from 'clsx';-import { twMerge } from 'tailwind-merge';+import { extendTailwindMerge } from 'tailwind-merge';++const twMerge = extendTailwindMerge({ extend: { classGroups: { shadow: ['shadow-1'] } } });​ export function cn(...inputs: ClassValue[]) { 	return twMerge(clsx(inputs)); }`;</script>​<DiffViewer	{patch}	lang="ts"	size="sm"	wrap	view="unified"	height="auto"	classes={{ viewport: 'max-h-64', header: 'bg-surface-container-low' }}	class="w-full"/>