Skip to Content
Components

Rich text editor

A minimal editor with an inline AI menu that streams a suggestion to accept or discard.

A minimal editor for a draft an agent helps with: a formatting toolbar (heading, bold, italic, inline code, link, lists and quote), the text, and a quiet foot with the word count and undo. Select text and press Ask AI — or ⌘J/Ctrl+J, or type / on a new line — and a suggestion streams in beside the text, the old words struck through and the new ones arriving word by word, to accept with Tab (or Enter) or discard with Esc. Pass your own ai to call a model; the default streams a canned demo.

Preview
0 wordsSelect text to ask AI

Usage

<!-- include core.css + rich-text-editor.css + rich-text-editor.js -->
<div class="rte" data-st="edit">
  <div class="rte-tb" role="toolbar" aria-label="Formatting"><!-- toolbar buttons --></div>
  <div class="rte-body" contenteditable="true" role="textbox" aria-multiline="true"
       data-placeholder="Write a draft, or type / on a new line for AI"><p><br></p></div>
  <div class="rte-ft"><!-- word count, undo/redo, review bar --></div>
</div>

Properties

NameTypeDescription
value / defaultValuestringThe text as HTML: paragraphs, one heading level (h2), lists, quotes, and inside them bold, italic, inline code and links. Anything else is cleaned out, as on paste.
onValueChange / onChange(html) => voidCalled with the HTML after each change the person makes. Not while a suggestion is on screen.
placeholderstringShown while the editor is empty.
labelstringThe text box's accessible name. Defaults to Draft.
hintReactNodeThe quiet hint beside the word count, on wide editors.
aiRichTextAi | falseWrites the AI's suggestions. Defaults to a demo that streams canned text; pass your model call, or false to hide the AI.
aiActionsRichTextAiAction[]The AI menu. Defaults to Improve writing, Make shorter, Fix grammar and Continue writing.
onSuggestion({ action, text, accepted }) => voidCalled when a suggestion is accepted or discarded.
refRichTextEditorHandleA handle to drive the editor: focus, getHTML, getText, setHTML, select, type, newLine, format, openAi, ask, accept, discard, undo, redo.

Theming

Works in light and dark (data-theme="dark") and in the Soft style (data-style="soft"). The editor keeps one height so it sits still, and its quiet text (word count, placeholder, struck words) comes up toward the foreground in dark mode to stay legible. The suggestion's new words use the accent tint with a blur-in that is reduced to an instant reveal under prefers-reduced-motion. In Soft the toolbar's active format and the suggestion pick up a hairline of the accent mark.

The Flutter port approximates the contenteditable surface (which the web uses natively): the body is a read-oriented rich view driven through a RichTextEditorController, with the same toolbar, word count and streamed Ask-AI flow. See the file header for the full list of approximations.

On this page