Vize

Rich Text

Headless rich-text editor: a typed document model (schema-declared node and mark types), immutable transactions and commands with undo history, input rules, sanitized HTML paste and serialization, and contenteditable parts.

Package @vizejs/ui/rich-text
Maturity stable
Own the source vize lib pull rich-text
Requires collection, context, id, positioner
Aliases wysiwyg, rich text editor, contenteditable editor, prose editor
Covers ProseMirror model, Tiptap, Lexical, Slate, WAI-ARIA textbox

Usage

import { RichTextRoot, RichText, RichTextBubbleMenu, RichTextContent, RichTextToolbar, RichTextToolbarButton } from "@vizejs/ui/rich-text";

Or copy the source into your project with vize lib pull rich-text (see Source Distribution).

API

RichTextRoot<Schema extends RichTextSchema>

Source: src/families/editor/rich-text/rich-text-root.vue

Props

Prop Type Default Description
schema (required) Schema undefined Schema declaring node and mark types; documents and commands are typed from it.
modelValue RichTextDoc<Schema> undefined Controlled document (v-model). undefined selects uncontrolled behavior.
defaultValue RichTextDoc<Schema> one empty default block Initial uncontrolled document.
id string | null undefined Consumer-owned editor id. null and undefined select a deterministic fallback.
editable boolean true Allow editing; false renders read-only content.
inputRules readonly RichTextInputRule[] defaultRichTextInputRules(schema) Input rules applied to typed text.
keymap RichTextKeymap defaultRichTextKeymap(schema) Keyboard shortcuts.
historyDepth number 100 Maximum undo depth.

Events

Event Payload Description
update:modelValue [value: RichTextDoc<Schema>] Fired with the new document after every change (supports v-model).
transaction [transaction: RichTextTransaction] Fired for every transaction, including selection-only changes.

Slots

Slot Slot props Description
default RichTextSlotProps<Schema> Editor parts (content, toolbars, bubble menus). Receives the live state and commands.

Exposed

Member Type Description
state RichTextState<Schema> Current editor state.
run (command: RichTextCommand) => boolean Run a command; returns whether it applied.
focus () => void Focus the editable content.
toHtml () => string Serialize the document to sanitized HTML.

RichText<Schema extends RichTextSchema>

Source: src/families/editor/rich-text/rich-text-root.vue

Props

Prop Type Default Description
schema (required) Schema undefined Schema declaring node and mark types; documents and commands are typed from it.
modelValue RichTextDoc<Schema> undefined Controlled document (v-model). undefined selects uncontrolled behavior.
defaultValue RichTextDoc<Schema> one empty default block Initial uncontrolled document.
id string | null undefined Consumer-owned editor id. null and undefined select a deterministic fallback.
editable boolean true Allow editing; false renders read-only content.
inputRules readonly RichTextInputRule[] defaultRichTextInputRules(schema) Input rules applied to typed text.
keymap RichTextKeymap defaultRichTextKeymap(schema) Keyboard shortcuts.
historyDepth number 100 Maximum undo depth.

Events

Event Payload Description
update:modelValue [value: RichTextDoc<Schema>] Fired with the new document after every change (supports v-model).
transaction [transaction: RichTextTransaction] Fired for every transaction, including selection-only changes.

Slots

Slot Slot props Description
default RichTextSlotProps<Schema> Editor parts (content, toolbars, bubble menus). Receives the live state and commands.

RichTextBubbleMenu

Source: src/families/editor/rich-text/rich-text-bubble-menu.vue

Props

Prop Type Default Description
ariaLabel string "Selection formatting" Accessible name of the floating toolbar.
placement Placement "top" Preferred placement relative to the selection.
offset number 8 Gap between the selection and the menu in CSS pixels.
open boolean undefined Force visibility; undefined shows the menu for a non-empty selection while editing.

Slots

Slot Slot props Description
default — Toolbar buttons for the selection.

Exposed

Member Type Description
element Readonly<ShallowRef<HTMLElement | null>> Template ref to the rendered root element.

RichTextContent

Source: src/families/editor/rich-text/rich-text-content.vue

Props

Prop Type Default Description
ariaLabel string undefined Accessible name of the text box.
ariaLabelledby string undefined Ids that label the text box.
ariaDescribedby string undefined Ids that describe the text box.
placeholder string undefined Placeholder published as data-placeholder (and aria-placeholder) while empty.
spellcheck boolean true Native spellchecking.

Exposed

Member Type Description
element HTMLDivElement | null Rendered editable element.
focus () => void Focus the editable element.

RichTextToolbar

Source: src/families/editor/rich-text/rich-text-toolbar.vue

Props

Prop Type Default Description
ariaLabel string "Formatting" Accessible name of the toolbar.

Slots

Slot Slot props Description
default — RichTextToolbarButton children (and separators or groups).

Exposed

Member Type Description
element Readonly<ShallowRef<HTMLElement | null>> Template ref to the rendered root element.

RichTextToolbarButton

Source: src/families/editor/rich-text/rich-text-toolbar-button.vue

Props

Prop Type Default Description
command (required) RichTextCommand undefined Command run on activation; the button disables itself while it cannot run.
active (state: RtState) => boolean undefined Toggle-state predicate; when provided the button publishes aria-pressed.
ariaLabel string undefined Accessible name when the content is an icon.
disabled boolean false Force the disabled state.

Slots

Slot Slot props Description
default RichTextToolbarButtonSlotProps Button contents. Receives active and disabled state.

Behavior

@vizejs/ui/rich-text is a headless, contenteditable rich-text editor. The model (schema, documents, transforms, transactions, history, commands, input rules, keymap, HTML) is pure TypeScript; the parts rich-text-root.vue, rich-text-content.vue, rich-text-toolbar.vue, rich-text-toolbar-button.vue, and rich-text-bubble-menu.vue bind it to the DOM. Every row is proven by the named test in rich-text.test.ts, rich-text-model.test.ts, rich-text-html.test.ts, or rich-text-ssr.test.ts; typing guarantees live in rich-text.types.test-d.ts.

# State Input Outcome Proven by
R1 any render rich-text-root.vue wraps rich-text-content.vue: role="textbox", aria-multiline, label, placeholder hooks, contenteditable, deterministic ids, editor hooks on every block renders an accessible multiline text box with editor hooks
R2 DOM selection map to/from model positions round-trip through text, nested marks, inline leaves, list paths, and element-level points DOM and model positions round-trip through text, marks, and leaves
R3 editable beforeinput insertText the browser mutation is prevented; the model applies the edit, re-renders, restores the caret, and emits update:modelValue beforeinput typing goes through the model, re-renders, and emits v-model
R4 editable typing, Enter, Backspace, Shift+Enter input rules, block splitting, joining, and hard breaks run through the keymap input rules, Enter, Backspace, and line breaks follow the keymap
R5 selection Mod-b, Mod-z, historyRedo formatting shortcuts, toolbar aria-pressed, undo and redo shortcuts format the selection and undo restores it
R6 editable paste clipboard HTML is parsed into schema nodes; scripts and event handlers never reach the DOM paste parses sanitized HTML and plain text into the document
R7 IME composition events the IME owns the DOM while composing; compositionend commits once; cancelled compositions restore the model's DOM composition text is committed once at compositionend and the DOM is repaired
R8 toolbar state, mousedown, click, arrows, Alt+F10, Escape rich-text-toolbar.vue and rich-text-toolbar-button.vue: disabled when a command cannot run, keep the selection, roving focus, Alt+F10 enters, Escape returns toolbar buttons reflect command state, keep focus, and rove with arrows
R9 focused, non-empty selection select rich-text-bubble-menu.vue shows a positioned toolbar and hides for a caret the bubble menu appears for a focused non-empty selection
R10 editable=false input, shortcuts aria-readonly, no mutations or events, tools disabled read-only editors refuse input and disable tools
R11 controlled typing / prop changes v-model round-trips the document controlled documents follow v-model and external replacements
R12 SSR two requests byte-identical static, escaped, sanitized HTML; bubble menu hidden renders the document as static, escaped, sanitized HTML on the server
R13 SSR hydrate no mismatch diagnostics; content node reused hydrates the rendered document without mismatches
R14 schema define names, textblock default, and list/listItem pairs are validated schemas validate names, a textblock default, and list pairs
R15 URLs links and images only safe schemes and relative URLs render URL guards allow safe schemes and relative URLs only
R16 documents validate structural validation against the schema (roles, lists, code blocks, marks) documents are validated structurally against the schema
R17 inline normalize / slice equal-mark runs merge; offsets slice across runs inline helpers merge equal marks and slice by offsets
R18 caret insertText / toggleMark active and stored marks apply to typed text typing inserts text with active and stored marks
R19 link type after it non-inclusive marks do not extend; unsafe hrefs never render links are not inclusive: typing after a link leaves it
R20 range toggleMark marks toggle across blocks; isMarkActive reports marks toggle across multi-block selections and report activity
R21 textblocks setBlockType / toggle type changes, toggles back, code blocks strip marks and refuse marks block types change, toggle back, and code blocks strip marks
R22 caret splitBlock split, headings continue as paragraphs, code inserts newlines Enter splits blocks, leaves headings as paragraphs, and inserts newlines in code
R23 list item splitBlock split items; an empty item leaves the list Enter in lists splits items and leaves the list from an empty item
R24 caret / range deleteBackward characters, surrogate pairs, words, selections, joins, heading reset Backspace deletes characters, surrogate pairs, words, selections, and joins blocks
R25 start of item/quote deleteBackward lift out of the first list item or quote; later items merge Backspace at the start of a list item or quote lifts it out
R26 caret deleteForward next character or merge the next block Delete removes the next character or merges the next block
R27 blocks wrapIn / toggleWrap lists and quotes wrap sibling blocks, unwrap, and map the selection wrapping in lists and quotes, toggling off, and mapping the selection
R28 caret insertInline images and hard breaks insert with sanitized attributes inline leaves insert at the caret with sanitized attributes
R29 history typing / format / undo / redo typing groups into one step, formatting is separate, redo replays history groups typing, separates formatting, and supports redo
R30 transactions create selections clamp; invalid documents are rejected transactions clamp selections and reject invalid documents
R31 small schema undeclared types commands for undeclared types do not apply commands for undeclared types are rejected at runtime
R32 paste model insertContent edge textblocks merge, middle blocks insert, code blocks receive plain text, list items split inserting content merges edge textblocks and keeps middle blocks
R33 serializer toHtml escaped text, shared mark elements, editor hooks and caret fillers serializes escaped text, nested marks, and editor hooks
R34 parser fromHtml schema nodes survive; scripts, handlers, unsafe URLs, and unknown elements are dropped or unwrapped parses pasted HTML into schema nodes and drops everything else
R35 parser empty input one empty default block parsing an empty fragment yields one empty block
R36 typing markdown prefixes #, >, -, 1., and ``` rules build structure only at block start block input rules turn markdown prefixes into structure
R37 typing delimiters **, _, `, and ~~ wrap text in marks mark input rules wrap delimited text and stop extending it
R38 code block typing no rules in code; a rule undoes in one step input rules are skipped in code blocks and undo as one step
R39 keys normalize Mod covers Ctrl/Cmd; physical digits for Alt chords key names normalize modifiers and physical digits
R40 keymap defaults marks, headings, lists, quotes, code, history, and editing keys the default keymap binds marks, headings, lists, and history

Notes

  • The browser never mutates the document: beforeinput is prevented and translated into commands. Composition is the exception; the IME's text is committed at compositionend and the DOM is re-rendered from the model.
  • Content renders through v-html of richTextToHtml(…, { editor: true }), which escapes text, drops event attributes, and passes every attribute through the schema's render specs (links and images are URL-checked).
  • Collaboration, tables, and drag-and-drop of content are out of scope for v1.