Theme studio

Design a theme, preview it live, then export it. Saved in this browser.

Quick picks #10b981
Generated scale
50100200300400500600700800900
Some text is below AA

RichTextEditor

  • Stable
  • WCAG 2.2 evidence
  • RTL

The shell of a rich-text field: label, an editor box with a formatting toolbar and a content slot, and a footer with helper text or an error and a counter. The library ships **no editor engine**: you render the editable element through children and wire its content and the tools' state; the shell never inserts content. Wiring the content slot (WCAG 1.3.1, 4.1.2): spread contentProps on your editable element. They give it role="textbox", aria-multiline="true", the label (aria-labelledby to the visible label, or aria-label), the helper / error / counter (aria-describedby), aria-invalid, aria-required, aria-disabled, aria-readonly and aria-placeholder, plus the content typography. Make the element non-editable while disabled or read-only. A click on the label or on the content padding focuses it. Focus in the content draws the brand border and halo on the editor box (2.4.7). Errors are text with an icon under the field, linked to the text box (1.4.1, 3.3.1); on a failed submit, move focus to the text box so the error is read with it. Paste is never blocked (3.3.7, 3.3.8). The label, toolbar and text wrap (1.4.10, 1.4.12). Consumer duties: label, helper, error and counter wording; validation; the editor engine, its keyboard shortcuts and its accessible output (headings, lists).

import { RichTextEditor } from "@nexera-ui/react";
Loading example…
4
examples
16
props
8
live controls
1
platform
10
WCAG criteria
1
block uses it

Try every prop. Copy the code.

Change the props and the code updates. Check light and dark, LTR and RTL, and phone width.

Loading…
description
errorMessage
placeholder
content
isInvalid
isDisabled
isReadOnly
isRequired
Generated code
import { RichTextEditor } from "@nexera-ui/react";

<RichTextEditor />

Examples 3

The same examples as Storybook, rendered live. Open Code to copy one.

Filled

Figma Content=Filled: a heading, a paragraph and a bullet list in the Figma typography.

Loading example…

Error and disabled

Figma State=Error (isInvalid + errorMessage, icon and text) and State=Disabled (isDisabled).

Loading example…

Long label right to left

A long label and helper text wrap; right-to-left mirrors the toolbar, the footer and the text.

Loading example…

Props 16

Press "Try it" on a card to load that prop into the playground.

16 props shown

children*Nexera
(props: RichTextEditorRenderProps) => ReactNode

Renders the editable content (the slot under the toolbar). The library ships no editor engine: render your own contenteditable element or your engine's root with contentProps on it, and keep its content and the tools' state yourself. The shell never inserts or changes content.

Default –
toolbarNexera
ReactNode

The formatting toolbar: usually an RTEToolbar. It is disabled with the editor.

Default –
descriptionNexera
ReactNode

Helper text under the editor, linked with aria-describedby. Hidden while an error message is shown.

Default –
errorMessageNexera
ReactNode

Error text under the editor, shown with an icon while isInvalid and linked with aria-describedby (WCAG 1.4.1, 3.3.1). Say what is wrong and how to fix it.

Default –
counterNexera
ReactNode

Character count at the end of the footer, for example "312 / 5,000"; linked with aria-describedby. Announce limit warnings yourself (announceStatus), not every keystroke.

Default –
placeholderNexera
string

Example text drawn over the empty content while content="empty", and exposed as aria-placeholder. Never the only label.

Default –
contentNexera
"empty" | "filled"

Whether the content is empty: the placeholder shows only while "empty". Keep it in sync with your editor.

Default empty
isInvalidNexera
boolean

Invalid: danger border, the error message instead of the helper text, aria-invalid.

Default false
isDisabledNexera
boolean

Disabled: dimmed label and text, sunken toolbar with every tool disabled, aria-disabled. Also make your element non-editable.

Default false
isReadOnlyNexera
boolean

Read-only: aria-readonly on the text box. Make your element non-editable.

Default false
isRequiredNexera
boolean

Required: aria-required on the text box. Validate on submit and set isInvalid with an errorMessage.

Default false
classNameNexera
string

Extra classes for the root, merged last so they win over the defaults.

Default –
styleNexera
CSSProperties

Inline style of the root.

Default –
labelNexera
FieldLabelContent

Visible label, linked to the control by React Aria (WCAG 1.3.1, 3.3.2). Short noun, for example "Work email". Wraps instead of truncating. A placeholder is never a substitute for it. Not set: the field has no visible label. Not set: the field has no visible label of its own.

Default –
aria-labelNexera
string

Accessible name when it must differ from the visible label. Prefer the visible label (WCAG 2.5.3). Accessible name. Required when there is no visible label; translate it. Accessible name. Optional when aria-labelledby is set.

Default –
aria-labelledbyNexera
string

Id(s) of element(s) that name the field; wins over the visible label. Id(s) of element(s) that name the field; wins over aria-label. Id(s) of visible element(s) that name the field.

Default –

* Required. React Aria props shown are the ones most apps use; the component accepts the rest of its React Aria props too.

Accessible by default.

Built on React Aria, and covered by the WCAG 2.2 evidence generated on every build.

WCAG 2.2 evidence

8 direct · 2 supporting
  • 1.3.1Info and RelationshipsLevel A · tested directly
  • 1.4.1Use of ColorLevel A · tested directly
  • 1.4.10ReflowLevel AA · supporting test
  • 1.4.12Text SpacingLevel AA · supporting test
  • 2.1.1KeyboardLevel A · tested directly
  • 2.4.3Focus OrderLevel A · tested directly
  • 2.4.7Focus VisibleLevel AA · tested directly
  • 3.3.1Error IdentificationLevel A · tested directly
  • 3.3.2Labels or InstructionsLevel A · tested directly
  • 4.1.2Name, Role, ValueLevel A · tested directly

Fits any width.

Nexera components respond to the space they are given. Drag the corner of the frame, or pick a width.

0 px · drag the corner

Styling hooks

Pass className to add Tailwind classes (merged last). State is exposed as data attributes, so you can style it with variants like data-pressed:.

  • data-disabled
  • data-focus-within
  • data-hovered
  • data-invalid
Usage
<RichTextEditor className="data-disabled:opacity-90 shadow-sm" />

Used in blocks

Related components