Rich text editor (WYSIWYG)
SvRichText: a lightweight WYSIWYG over a contentEditable region - bold/italic/underline/strike, headings, lists, quote, code block, alignment, links, undo/redo - emitting HTML via bind:value, with a configurable toolbar. Parity: Smart editor.
A live, editable Svelte 5 component example from the SvGrid gallery (Inputs). Read the SvGrid UI component docs for the full API.
About this example
SvRichText, a lightweight WYSIWYG editor for Svelte 5 over a contentEditable region: bold, italic, underline and strikethrough, headings, lists, quotes, code blocks, alignment, links and undo and redo, emitting HTML through bind:value with a configurable toolbar, placeholder and minimum height.
SvRichText - a lightweight WYSIWYG editor (bold/italic/underline/strike, headings, lists, quote, code, align, link) over a contentEditable region, emitting HTML. Bindable value; toolbar is configurable. Parity: Smart editor.
Imports, features and API used
Imports: @svgrid/grid
Frequently asked questions
What does bind:value contain?
The editor's HTML as a string, updated on every change. Set it to load existing content and read it to save.
Can I limit the toolbar?
Yes. The tools prop takes the list of commands to show, so a comment box can offer only bold, italic and link while a CMS field shows everything.
Is the HTML sanitized?
The editor only emits markup produced by its own commands, but content pasted in or loaded from a server should still be sanitized on the server before it is stored or rendered elsewhere.
Related documentation
Related articles
- A Right-Click Context Menu for Your Svelte Data Grid - Build a right-click context menu that wires row actions, handles multi-row selection, and stays out of the way of SvGrid's own event handling.
- An Editable Select / Dropdown Cell in SvGrid - Build a dropdown cell editor in SvGrid using a Svelte 5 snippet - constrain user input to a fixed option set while keeping sort, filter, and undo working on the underlying value.
- SvGrid UI Components Tips and Tricks: Svelte 5 Buttons, Inputs, Tree, Forms - Practical tips for the SvGrid UI component kit - SvButton states, a Cmd+K command palette, a virtualized searchable tree, schema-driven forms, a parsing date/time field, and the shared field contract - each with a code snippet.
Source code (341-rich-text-editor.svelte)
<script lang="ts">
/**
* SvRichText - a lightweight WYSIWYG editor (bold/italic/underline/strike,
* headings, lists, quote, code, align, link) over a contentEditable region,
* emitting HTML. Bindable value; toolbar is configurable. Parity: Smart editor.
*/
import { SvRichText } from '@svgrid/grid'
let html = $state(
'<h2>Release notes</h2><p>This release adds <b>rich text</b>, <i>recurrence</i> and a <a href="#">command palette</a>.</p><ul><li>Faster rendering</li><li>New editors</li></ul><blockquote>Ship early, ship often.</blockquote>',
)
let notes = $state('')
</script>
<div class="wrap">
<header>
<h2>Rich text editor</h2>
<p><code>SvRichText</code> emits HTML you can store or render. The toolbar formats the selection; <code>bind:value</code> keeps your state in sync.</p>
</header>
<div class="cols">
<div>
<div class="lbl">Editor</div>
<SvRichText bind:value={html} placeholder="Write somethingā¦" minHeight="220px" />
</div>
<div>
<div class="lbl">Emitted HTML</div>
<pre class="out">{html}</pre>
</div>
</div>
<div class="mini">
<div class="lbl">Compact toolbar (custom tools)</div>
<SvRichText bind:value={notes} placeholder="A quick noteā¦" minHeight="90px" tools={['bold', 'italic', 'underline', '|', 'ul', 'ol', '|', 'link', 'clear']} />
</div>
</div>
<style>
.wrap { padding: 20px; max-width: 960px; display: flex; flex-direction: column; gap: 20px; }
header h2 { margin: 0 0 4px; font-size: 20px; font-weight: 700; }
header p { margin: 0; color: var(--sg-muted, #64748b); font-size: 13.5px; }
header code, .lbl code { background: var(--sg-row-hover-bg, #f1f5f9); padding: 1px 5px; border-radius: 4px; font-size: 12px; }
.cols { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 18px; }
@media (max-width: 760px) { .cols { grid-template-columns: 1fr; } }
.lbl { font-size: 12px; font-weight: 600; color: var(--sg-muted, #64748b); margin-bottom: 6px; }
.out {
margin: 0; height: 264px; overflow: auto; padding: 12px; border-radius: 8px; white-space: pre-wrap; word-break: break-word;
background: var(--sg-row-hover-bg, #f8fafc); border: 1px solid var(--sg-border, #e2e8f0);
font: 12px/1.6 ui-monospace, "SF Mono", Menlo, monospace; color: var(--sg-fg, #334155);
}
.mini { max-width: 560px; }
</style>More Inputs examples
- Text inputs - Typed text controls: SvNumberInput (min/max/step, grouping, precision, spinners), SvPasswordInput (reveal + strength), SvMaskedInput (pattern mask), SvPhoneInput (country dial code + national mask) and SvColorInput (swatch + palette popover). Each a SvGrid cell editor, standalone too.
- Input adornments - Leading/trailing icon snippets and prefix/suffix text affixes on SvTextInput, plus the shared clear button, sizes and invalid/readonly/disabled states - all owned by SvField's frame chrome so the whole text-input family behaves identically.
- Number input - SvNumberInput on its own: min/max/step, thousands grouping, precision, prefix/suffix, spinner buttons and the shared field contract (label, hint, required/error validation).
- Password input - SvPasswordInput on its own: a reveal toggle and an optional 4-level strength meter, with localizable strings and the shared field contract.
- Masked input - SvMaskedInput on its own: a fixed-pattern mask (# digit, A letter, * alphanumeric; other chars literal) emitting the masked + raw value and a complete flag.