Custom Rendering
SvelteDiff offers two customization levels. rendererClasses preserves the built-in markup and changes its classes. Snippets replace the markup itself.
Class-based styling
<SvelteDiff
{originalText}
{modifiedText}
rendererClasses={{
remove: 'change change--removed',
insert: 'change change--inserted',
equal: 'change change--equal',
expected: 'change change--expected'
}}
/><SvelteDiff
{originalText}
{modifiedText}
rendererClasses={{
remove: 'change change--removed',
insert: 'change change--inserted',
equal: 'change change--equal',
expected: 'change change--expected'
}}
/>:global(.change--removed) {
background: #fee2e2;
color: #991b1b;
text-decoration: line-through;
}
:global(.change--inserted) {
background: #dcfce7;
color: #166534;
}
:global(.change--expected) {
background: #dbeafe;
border-bottom: 1px dashed #2563eb;
}:global(.change--removed) {
background: #fee2e2;
color: #991b1b;
text-decoration: line-through;
}
:global(.change--inserted) {
background: #dcfce7;
color: #166534;
}
:global(.change--expected) {
background: #dbeafe;
border-bottom: 1px dashed #2563eb;
}The classes are applied only to the built-in <span> for that segment. A missing class falls back to the component’s inline default for removed, inserted, or expected text.
Direct child snippets
Use snippets when semantic HTML or richer UI matters:
<SvelteDiff {originalText} {modifiedText}>
{#snippet remove(text: string)}
<del aria-label="removed">{text}</del>
{/snippet}
{#snippet insert(text: string)}
<ins aria-label="inserted">{text}</ins>
{/snippet}
{#snippet expected(text: string, groupName: string)}
<mark title={`Expected ${groupName}`}>{text}</mark>
{/snippet}
{#snippet lineBreak()}
<br />
{/snippet}
</SvelteDiff><SvelteDiff {originalText} {modifiedText}>
{#snippet remove(text: string)}
<del aria-label="removed">{text}</del>
{/snippet}
{#snippet insert(text: string)}
<ins aria-label="inserted">{text}</ins>
{/snippet}
{#snippet expected(text: string, groupName: string)}
<mark title={`Expected ${groupName}`}>{text}</mark>
{/snippet}
{#snippet lineBreak()}
<br />
{/snippet}
</SvelteDiff>Renderer maps
Snippets can also be assembled into a renderers object. This is useful when a design system owns reusable diff renderers.
{#snippet removed(text: string)}<del class="removed">{text}</del>{/snippet}
{#snippet inserted(text: string)}<ins class="inserted">{text}</ins>{/snippet}
<SvelteDiff
{originalText}
{modifiedText}
renderers={{ remove: removed, insert: inserted }}
/>{#snippet removed(text: string)}<del class="removed">{text}</del>{/snippet}
{#snippet inserted(text: string)}<ins class="inserted">{text}</ins>{/snippet}
<SvelteDiff
{originalText}
{modifiedText}
renderers={{ remove: removed, insert: inserted }}
/>Mixing strategies
Resolution is per type. A direct insert snippet can coexist with renderers.remove; equal text can still use the built-in fallback.
<SvelteDiff {originalText} {modifiedText} renderers={{ remove: sharedRemove }}>
{#snippet insert(text: string)}
<AnimatedInsertion>{text}</AnimatedInsertion>
{/snippet}
</SvelteDiff><SvelteDiff {originalText} {modifiedText} renderers={{ remove: sharedRemove }}>
{#snippet insert(text: string)}
<AnimatedInsertion>{text}</AnimatedInsertion>
{/snippet}
</SvelteDiff>The direct child snippet wins for insert. The renderer map is used for remove. Everything else falls back to built-in rendering.
Line breaks
The component splits multiline segments and calls lineBreak between lines. Override it when your output needs block separation, line numbers, or accessible separators.
Keep white-space: pre-wrap on the surrounding output if preserving other whitespace matters.