Skip to main content

RichTextEditor

Implemented

Rich text editor for event descriptions. Replaces TinyMCE. TipTap/Lexical wrapper.

Categorymolecules
Sourcepatterns/molecules/richtexteditor.html
AI HintUse for grouped UI patterns. Combine atoms into functional units.

WYSIWYG rich-text editor for form descriptions — a contentEditable surface driven by a TextFormatToolbar (bold/italic/headings/lists/link/quote/code/undo/redo via document.execCommand). Produces HTML; sanitize it before rendering anywhere.

RichTextEditor

molecules

Controlled or uncontrolled editor with an owned label/editor/hint stack. The surface is a role="textbox" contentEditable div; the sibling TextFormatToolbar is wired via aria-controls. Output is raw HTML — the onChange payload MUST be sanitized at render time (DOMPurify or equivalent).

Event description

Formatting is kept as HTML in the stored value.

This component supports light and dark mode.

Usage

RichTextEditor requires onChange — a bare <RichTextEditor /> render throws on the first keystroke. The controlled pattern pairs value with local state; the uncontrolled pattern seeds defaultValue and listens to onChange. The emitted HTML must be sanitized before rendering it anywhere (see Sanitization).

import { RichTextEditor } from '@epasslive/design-system'

const [description, setDescription] = useState('')

<RichTextEditor
  id="description"
  label="Event description"
  helperText="Basic formatting is allowed."
  value={description}
  onChange={setDescription}
  placeholder="Describe the event…"
  minHeight={220}
  commands={['undo', 'redo', 'bold', 'italic', 'h2', 'h3', 'ul', 'ol', 'link']}
/>

Props

Prop
Type
Default
Description
value
string
—

Controlled editor content (HTML). Setting it re-renders the surface (guarded against cursor-jump loops).

defaultValue
string
""

Initial content when uncontrolled.

onChange*
(html: string) => void
—

Called with the surface innerHTML on every input and after every toolbar command. Required — omitting it crashes on first interaction.

placeholder
string
"Start typing…"

Placeholder text shown via data-placeholder + :empty pseudo-content. Defaults to English; pass a localized string in non-English locales.

disabled
boolean
false

Disables editing and the toolbar (aria-disabled, opacity 50).

readOnly
boolean
false

Non-editable preview mode (aria-readonly, no contentEditable).

commands
TextFormatCommand[]
compact document set

Toolbar command subset — "bold" | "italic" | "underline" | "strikethrough" | "h1" | "h2" | "h3" | "ul" | "ol" | "link" | "quote" | "code" | "undo" | "redo". Defaults to a 12-command document set.

minHeight
number
200

Minimum surface height in px (inline style; the surface grows with content).

linkPrompt
string
"Enter URL:"

Prompt string shown by the link command. Localization escape hatch — the DS has no i18n mechanism yet.

ariaLabel
string
—

Accessible name for the editor surface. Optional when label is set (aria-labelledby wiring takes over); an explicit ariaLabel wins over the visible label.

label
string
—

Visible field label above the toolbar. The component owns the label/editor/hint stack — do not hand-roll a sibling label.

required
boolean
false

Marks the field required; appends an aria-hidden * to the label and aria-required to the surface.

helperText
string
—

Helper text below the editor (rendered via the Caption atom), wired with aria-describedby.

hint
string
—

Alias for helperText (dashboard-form naming).

error
string
—

Error message; replaces the helper text and marks the surface aria-invalid (role="alert").

className
string
—

Additional CSS classes on the wrapper div.

id
string
—

Stable id for the editor surface (defaults to an auto-generated rte-* id; derives the label/helper/error ids).

Props

Preview

RichTextEditor

Prop
Type
Required
Default
Description
value
string
Optional
—

Editor content (HTML)

onChange
(html: string) => void
Required
—

Content change handler

disabled
boolean
Optional
false

Disabled state

placeholder
string
Optional
—

Placeholder text

defaultValue
string
Optional
''

Initial value when uncontrolled.

readOnly
boolean
Optional
false

Makes the value read-only.

commands
TextFormatCommand[]
Optional
—

Synced from source — no interface JSDoc yet; document on `RichTextEditor.tsx`.

minHeight
number
Optional
200

Synced from source — no interface JSDoc yet; document on `RichTextEditor.tsx`.

ariaLabel
string
Optional
—

Synced from source — no interface JSDoc yet; document on `RichTextEditor.tsx`.

label
string
Optional
—

Visible label for the control.

required
boolean
Optional
false

Marks the field as required.

helperText
string
Optional
—

Helper text rendered below the control.

hint
string
Optional
—

Alias for `helperText`.

error
string
Optional
—

Error message; takes precedence over helper text.

linkPrompt
string
Optional
'Enter URL:'

Prompt string for the link command (i18n escape hatch).

className
string
Optional
—

Additional CSS classes on the wrapper div.

id
string
Optional
—

Stable id; derives the label/helper/error ids.

Quick use:<RichTextEditor />

Accessibility

ARIA roletextbox

ARIA

2 rules

WAI-ARIA APG

Editable area must have role='textbox' and aria-multiline='true'

WCAG 1.1.1

Toolbar buttons must have aria-label

Keyboard

1 rule

WCAG 2.1.1

Tab navigates toolbar, Ctrl+B/I/U keyboard shortcuts work

validation

1 rule

OWASP XSS

Output HTML must be sanitized (DOMPurify or equivalent)

Usage

Import

tsxSnippet
import { RichTextEditor } from "@epasslive/design-system"

Example 1

tsxSnippet
<RichTextEditor />

Example 2

tsxSnippet
<RichTextEditor variant="default" />

Anti-patterns

— Do not hardcode data in molecules

— Avoid complex business logic in molecules

Design System v4.1.0 | ePassLive