Components

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.

Available since@crisp-ui-kit/crisp@0.11.0

Installation

npm install @crisp-ui-kit/crisp

Usage

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

Available since@crisp-ui-kit/crisp@0.69.0

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.

PropTypeEffect
variant"note" | "comment"Selects the comment editor. Default "note".
onInsertMedia() => voidShows "Insert media..." in the / menu. The host does the upload.
onAttachFiles() => voidShows "Attach files..." in the / menu and handles Cmd Shift U. The host does the upload.
onCreateIssueFromSelection(text: string) => voidShows "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 passes onInsertMedia / onAttachFiles and 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 onCreateIssueFromSelection is passed. It is centred on the selection, kept inside the viewport.
  • : emoji: :smi lists 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, where event.defaultPrevented tells 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:

BlockMarkup
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.

On this page