Skip to content
Protocore Design Systemv1.6.9

/// Inputs

RichTextEditor

A bordered, sunken WYSIWYG surface with a mono formatting toolbar, built on tiptap. Controlled via value/onChange.

import { RichTextEditor } from "@protocore/pds";
View as Markdown

Optional peer dependencies

RichTextEditor ships on the `@protocore/pds/editor` subpath so its editor engine stays out of the main bundle — exactly like charts keep recharts out. It requires the optional tiptap peers, installed alongside @protocore/pds:

npm i @tiptap/react @tiptap/pm @tiptap/starter-kit @tiptap/extension-link @tiptap/extension-placeholder

Import it from the subpath: import { RichTextEditor } from "@protocore/pds/editor";

Controlled editor

Pass value + onChange to control the document. The toolbar toggles bold, *italic*, strike, inline code, a heading, bullet / numbered lists, blockquote, and links; the active mark inverts to the solid fill. All the usual keyboard shortcuts (⌘B, ⌘I, …) come from tiptap.

Comment composer

A compact editor with a lower minHeight, wired to a submit row. Strip the tags to detect an empty document before enabling the action.

Read only

Set readOnly to present stored rich text without an editing affordance — the toolbar disables and the surface is not editable, but content still renders with the document typography.

When to use it

Reach for RichTextEditor when authors need *formatted* prose — headings, lists, links, emphasis — such as CMS body content, changelog entries, or support replies. For plain, unformatted multi-line text (a note, a description, an address) use Textarea: it is lighter, has no peer dependencies, and posts a simple string. For read-only display of code, use CodeBlock.

Usage

Do

  • Install the optional `@tiptap/*` peers, and import from `@protocore/pds/editor`.
  • Give the editor an accessible name via `aria-label` (or an associated `Field` label).
  • Keep the document controlled with `value` / `onChange`, or uncontrolled with `defaultValue` — not a mix.
  • Persist and re-hydrate with a single serialization — HTML *or* JSON via `format` — consistently.

Don't

  • Don't import RichTextEditor from the main `@protocore/pds` entry — it lives on the `/editor` subpath.
  • Don't use it for plain text — a Textarea is the right, dependency-free control.
  • Don't feed `value` a string in a different `format` than you configured — the editor can't parse it.
  • Don't rely on it rendering without the peers installed; the subpath is intentionally dependency-gated.

Accessibility

KeysAction
⌘/Ctrl + BToggle bold on the selection.
⌘/Ctrl + IToggle italic on the selection.
TabMove focus from the surface out of the editor (does not insert a tab).
EnterNew paragraph; inside a list, a new list item.
Backspace at line startLift a list item / blockquote back to a paragraph.
  • The writing surface exposes `role="textbox"` with `aria-multiline="true"` and the `aria-label` you provide.
  • Every toolbar button is icon-only with an `aria-label`, sits in a `role="toolbar"` group, and carries `aria-pressed` reflecting whether its mark/node is active at the cursor.
  • In `readOnly` mode the toolbar buttons are `disabled` and the surface is not editable.
  • Toolbar buttons preventDefault on mousedown so clicking one keeps the text selection, applying the command to the intended range.

RichTextEditor props

PropTypeDefaultDescription
valuestringControlled document value. HTML by default; a stringified ProseMirror JSON when `format="json"`. Pair with `onChange`.
defaultValuestringUncontrolled initial value, in the same serialization as `value`.
onChange(value: string) => voidFired with the serialized document on every edit (HTML, or JSON when `format="json"`).
format"html" | "json""html"Serialization used for `value` / `onChange`.
placeholderstringMuted placeholder shown while the document is empty.
readOnlybooleanfalseRender read-only — the toolbar disables and the surface is not editable.
toolbarbooleantrueShow the formatting toolbar.
minHeightnumber180Minimum height of the writing surface, in px.
aria-labelstring"Rich text editor"Accessible name for the editable region.
editorRef(editor: Editor | null) => voidEscape hatch — receives the underlying tiptap `Editor` instance once ready (or `null`).

Related

  • TextareaSunken multi-line text control, with an opt-in auto-resize that grows the field to fit its content.
  • InputThe base single-line text control — a sunken field with optional leading and trailing adornment slots.
  • CodeBlockMonospace code panel — sunken surface, hairline border, and an optional title bar.
  • FieldForm-field wrapper: a caption label, the control, and hint or error messaging with ARIA wired for you.