Diff
Line-based text diff viewer with inline and side-by-side display modes.
<arc-diff>Overview
Diff compares two blocks of text line by line and renders a color-coded visualization of the changes. Added lines appear with a green-tinted background and a + prefix, removed lines appear with a red-tinted background, strikethrough text, and a - prefix, and unchanged lines display in a muted secondary color with a blank prefix.
The component uses a longest-common-subsequence algorithm to compute the minimal diff between the before and after text props. No external diff libraries are required. The algorithm runs entirely inside the component.
Two display modes are available via the mode prop. The default inline mode renders all changes in a single scrollable column, interleaving additions and removals in reading order. The side-by-side mode splits the view into a CSS grid with two equal-width panes separated by a subtle border. The left pane shows the original text (with removals highlighted) and the right pane shows the modified text (with additions highlighted).
Line numbers are displayed in a fixed-width gutter column. The entire viewer uses the monospace font stack and code-sized typography tokens for a terminal-like reading experience. The container supports horizontal scrolling for long lines with thin styled scrollbars.
Diff is designed for code review panels, changelog overlays, configuration comparisons, and anywhere a developer-facing UI needs to show what changed between two versions of text content.
Guidelines
When to use
- Use short, focused text snippets. Diffs work best with fewer than 100 lines
- Set mode="side-by-side" when horizontal space allows for easier comparison
- Pair with a heading or label to describe what is being compared
- Use for code snippets, configuration files, or any line-oriented text
- Wrap in a container with a max-height and overflow-y: auto for very long diffs
When not to use
- Do not pass binary or non-text content as before/after values
- Do not use Diff for single-character or word-level highlighting. It operates on whole lines
- Do not embed interactive elements inside the before or after strings
- Do not use side-by-side mode in narrow containers where columns would be too cramped
Features
- LCS-based line diff algorithm with no external dependencies
- Inline mode interleaves additions and removals in a single column
- Side-by-side mode renders two panes in a CSS grid with a subtle divider
- Color-coded lines: green for added, red with strikethrough for removed, muted for unchanged
- Line number gutter with non-selectable numbers for clean copy-paste
- Prefix markers (+, -, space) in a fixed-width column for scanning
- Monospace font stack with code-size typography tokens
- Horizontal scroll with thin styled scrollbars for long lines
- Exposed CSS parts (container, line, line-number, prefix) for external style overrides
- Respects `prefers-reduced-motion` for transitions
Preview
Usage
<arc-diff
original="const name = 'World';
console.log('Hello');
return name;"
revised="const name = 'ARC UI';
console.log('Hello');
console.log('Welcome');
return name;">
</arc-diff>
<!-- Side-by-side mode -->
<arc-diff mode="side-by-side"
original="line one
line two
line three"
revised="line one
line 2
line three
line four">
</arc-diff>import { Diff } from '@arclux/arc-ui-react';
export default function Example() {
return (
<>
<Diff
original={`const name = 'World';
console.log('Hello');
return name;`}
revised={`const name = 'ARC UI';
console.log('Hello');
console.log('Welcome');
return name;`}
/>
{/* Side-by-side mode */}
<Diff mode="side-by-side"
original="line one\nline two\nline three"
revised="line one\nline 2\nline three\nline four"
/>
</>
);
}<script setup>
import { Diff } from '@arclux/arc-ui-vue';
const original = `const name = 'World';
console.log('Hello');
return name;`;
const modified = `const name = 'ARC UI';
console.log('Hello');
console.log('Welcome');
return name;`;
</script>
<template>
<Diff :original="original" :revised="modified" />
<Diff mode="side-by-side" :original="original" :revised="modified" />
</template><script>
import { Diff } from '@arclux/arc-ui-svelte';
const original = `const name = 'World';
console.log('Hello');
return name;`;
const modified = `const name = 'ARC UI';
console.log('Hello');
console.log('Welcome');
return name;`;
</script>
<Diff original={original} revised={modified} />
<Diff mode="side-by-side" original={original} revised={modified} />import { Component } from '@angular/core';
import { Diff } from '@arclux/arc-ui-angular';
@Component({
imports: [Diff],
template: `
<arc-diff
[before]="original"
[after]="modified">
</arc-diff>
<arc-diff mode="side-by-side"
[before]="original"
[after]="modified">
</arc-diff>
`,
})
export class MyComponent {
original = `const name = 'World';
console.log('Hello');
return name;`;
modified = `const name = 'ARC UI';
console.log('Hello');
console.log('Welcome');
return name;`;
}import { Diff } from '@arclux/arc-ui-solid';
const original = `const name = 'World';
console.log('Hello');
return name;`;
const modified = `const name = 'ARC UI';
console.log('Hello');
console.log('Welcome');
return name;`;
<Diff original={original} revised={modified} />
<Diff mode="side-by-side" original={original} revised={modified} />import { Diff } from '@arclux/arc-ui-preact';
const original = `const name = 'World';
console.log('Hello');
return name;`;
const modified = `const name = 'ARC UI';
console.log('Hello');
console.log('Welcome');
return name;`;
<Diff original={original} revised={modified} />
<Diff mode="side-by-side" original={original} revised={modified} />API
originalstring''- The original text to compare (split by newlines).
revisedstring''- The modified text to compare (split by newlines).
mode'inline' | 'side-by-side''inline'- Display mode: 'inline' renders changes in a single column, 'side-by-side' renders two panes in a grid.