RichText
The block note editor — a contentEditable rich-text editor.
A Notes editor: a large 32/36 title, a 14/20 prose body (headings, lists, quote, code, marks, links), and a floating toolbar that appears over a text selection. Built on contentEditable so it's dependency-free — no ProseMirror/Slate. Uncontrolled: seed with defaultTitle/defaultHtml and read changes via onChange.
Its chrome and toolbar are measured from a live Notes editor, but the content is entirely yours — use it directly for any rich-text note.
Select some text in the preview to reveal the formatting toolbar.
Installation
npm install @crisp-ui-kit/crispUsage
import { RichTextEditor } from "@crisp-ui-kit/crisp";
<RichTextEditor
defaultTitle="Kickoff notes"
defaultHtml="<p>Start writing…</p>"
onChange={({ title, html }) => save({ title, html })}
/>;Props
Prop
Type
The default ("note") selection toolbar covers bold, italic, underline, strikethrough, link, H1, H2, bullet list, numbered list, and quote.
Mentions
renderMentionPicker detects an "@query" trigger and hands back its anchor, but draws no picker of its own — the content varies too much by app to have one measured shape. MentionMenu is the built-in, floating, grouped-section popup to build one from (crisp#646, #1009); see this page's own example above, or MentionMenu's own docs for the standalone shape.
Comment variant
variant="comment" is the editor of a comment box rather than a note, modelled on Linear's composer (crisp#1337, #1338). "note" (the default) is unchanged.
| Prop | Type | Effect |
|---|---|---|
variant | "note" | "comment" | Selects the comment editor. Default "note". |
onInsertMedia | () => void | Shows "Insert media..." in the / menu. The host does the upload. |
onAttachFiles | () => void | Shows "Attach files..." in the / menu and handles Cmd Shift U. The host does the upload. |
onCreateIssueFromSelection | (text: string) => void | Shows "Create issue from selection" in the selection toolbar, called with the selected text. |
- Input rules:
**bold**,_italic_and`code`convert as you type; a bare URL becomes a link when a space or Enter follows.~~strike~~does not convert (Linear's doesn't either). /menu: Linear's own, in its groups: Heading 1-3; Bulleted list, Numbered list, Checklist; Insert media, Attach files; Code block, Diagram, LaTeX math block, Collapsible section, Blockquote. Each shows its shortcut. Insert media and Attach files appear only when the host passesonInsertMedia/onAttachFilesand do the upload themselves. The menu opens below the caret, or above it when it does not fit below and there is more room above.- Selection toolbar: text style (Regular text, Heading 1-3), bold, italic, strikethrough, underline, link, quote, collapsible section, inline code, code block, list (List, Numbered list, Checklist), and "Create issue from selection" when
onCreateIssueFromSelectionis passed. It is centred on the selection, kept inside the viewport. :emoji::smilists up to 12 matches as:shortcode:. Like the/menu, it opens below the caret and flips above when there is more room there (crisp#1340).- Shortcuts: Cmd B / I / U, Cmd Shift X (strike), Cmd E (inline code), Cmd K (link), Cmd Alt 0-3 (text style), Cmd Shift 6 (collapsible), 7 (checklist), 8 / 9 (lists), Cmd Shift \ (code block), Cmd Shift U (attach), Alt Shift . (blockquote).
- Toolbar dropdowns: the text-style and list dropdowns are placed with
placeFloating, so they line up with the trigger's inline-start edge under RTL, and take the shared menu keyboard (arrows, Home/End, typeahead, Escape, Tab). The collapsible and checklist toggle zones are measured from the inline-start edge too (crisp#1343). - Enter is the host's. crisp never handles Enter or Shift+Enter; keydown bubbles to the root's
onKeyDown, whereevent.defaultPreventedtells you an open/or:menu already used it. - Not built: Ask agent (the host has no agent) and Heading 4.
Stored HTML
What the host's sanitizer has to allow, beyond p, h1-h3, blockquote, ul/ol/li, strong, em, u, s, code, a and the mention chip:
| Block | Markup |
|---|---|
| Checklist | <ul data-type="checklist"><li data-checked="true|false">…</li></ul> |
| Collapsible section | <details open><summary>…</summary>…</details> |
| Code block | <pre>…</pre> |
| LaTeX math block | <pre data-type="latex">source</pre> |
| Diagram | <pre data-type="mermaid">source</pre> |
A LaTeX or diagram block shows its source while the caret is in it and its render otherwise; only the source is stored. The renders need the optional peers katex and mermaid, which are loaded on first use (the editor bundle does not contain them); without them the block stays in its source view.