RTETool
- Needs review
- WCAG 2.2 evidence
- RTL
- Web · iOS · Android
One formatting tool of a rich-text toolbar: a square icon button with a tooltip that names it and
shows its keyboard shortcut. Built on React Aria ToggleButton (kind="toggle", the default: aria-pressed
reflects whether the format is on at the caret) or Button (kind="action"). Enter and Space activate it; inside an
RTEToolbar or RTEBubbleMenu the arrow keys, Home and End move between tools and the toolbar is one tab stop
(WCAG 2.1.1, 4.1.2). The pressed state is shown by a filled, outlined box, not by colour alone (1.4.1, 1.4.11); keyboard
focus draws the focus ring (2.4.7). 32 px on web, 50 pt / 52 dp on iOS / Android (2.5.8). The library ships no editor engine: the consumer keeps isSelected in sync with the editor selection and runs the
command in onChange / onPress. Pass preventFocusOnPress so a pointer press keeps the caret and selection in
the editor (keyboard users are in the toolbar, so restore the editor selection before running the command).
Consumer duties: translated aria-label, the shortcut text and its registration, and explaining why a tool is
disabled when it matters.
import { RTETool } from "@nexera-ui/react";- 5
- examples
- 23
- props
- 6
- live controls
- 3
- platforms
- 9
- 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.
import { RTETool } from "@nexera-ui/react";
<RTETool />Examples 4
The same examples as Storybook, rendered live. Open Code to copy one.
States
Toggle tools (aria-pressed) off, on, disabled, and an action tool (kind="action", no pressed state). Direction-bearing icons such as Undo sit in DirectionalIcon.
import { DirectionalIcon, RTETool } from "@nexera-ui/react";
import { LuBold, LuHighlighter, LuItalic, LuList, LuUndo2 } from "react-icons/lu";
export function States() {
return (
<div className="flex flex-wrap items-center gap-2">
<RTETool
aria-label="Italic"
icon={<LuItalic />}
shortcut="Ctrl+I"
aria-keyshortcuts="Control+I"
/>
<RTETool
aria-label="Bold"
icon={<LuBold />}
defaultSelected
shortcut="Ctrl+B"
aria-keyshortcuts="Control+B"
/>
<RTETool aria-label="Highlight" icon={<LuHighlighter />} isDisabled />
<RTETool aria-label="Bullet list" icon={<LuList />} isDisabled defaultSelected />
<RTETool
kind="action"
aria-label="Undo"
icon={
<DirectionalIcon>
<LuUndo2 />
</DirectionalIcon>
}
shortcut="Ctrl+Z"
aria-keyshortcuts="Control+Z"
/>
</div>
);
}
Platforms
Figma RTE toolbar Mobile tools: 32 px on web, 50 pt on iOS, 52 dp on Android, with 20 px icons on mobile.
import { RTETool } from "@nexera-ui/react";
import { LuBold, LuItalic, LuKeyboard } from "react-icons/lu";
const platforms = ["web", "ios", "android"] as const;
export function Platforms() {
return (
<div className="flex flex-col gap-4">
{platforms.map((platform) => (
<div key={platform} className="flex flex-wrap items-center gap-2">
<span className="w-16 text-body-caption text-secondary">{platform}</span>
<RTETool
platform={platform}
aria-label={`Bold (${platform})`}
icon={<LuBold />}
defaultSelected
/>
<RTETool platform={platform} aria-label={`Italic (${platform})`} icon={<LuItalic />} />
<RTETool
platform={platform}
kind="action"
aria-label={`Hide keyboard (${platform})`}
icon={<LuKeyboard />}
/>
</div>
))}
</div>
);
}
Right to left
Right-to-left with translated names and a long tooltip that wraps; the Undo icon mirrors.
import { DirectionalIcon, RTETool } from "@nexera-ui/react";
import { LuBold, LuHighlighter, LuUndo2 } from "react-icons/lu";
export function RightToLeft() {
return (
<div className="flex flex-wrap items-center gap-2">
<RTETool
aria-label="غامق"
icon={<LuBold />}
shortcut="Ctrl+B"
aria-keyshortcuts="Control+B"
/>
<RTETool
aria-label="تمييز"
tooltip="تمييز النص المحدد بلون الخلفية لجعل الجمل المهمة أسهل في الملاحظة"
icon={<LuHighlighter />}
/>
<RTETool
kind="action"
aria-label="تراجع"
icon={
<DirectionalIcon>
<LuUndo2 />
</DirectionalIcon>
}
/>
</div>
);
}
In editor
A realistic composed editor with a plain contenteditable element (the tools in a toolbar, wired to the selection with isSelected and onChange).
export function InEditor() {
return <RichTextEditorDemo />;
}
Props 23
Press "Try it" on a card to load that prop into the playground.
23 props shown
icon*NexeraReactNodeThe icon. Decorative: the tool is named by
aria-label or aria-labelledby. 16 px on web, 20 px on iOS and Android (mobile size UNVERIFIED). Wrap
direction-bearing glyphs (undo, redo, indent) in DirectionalIcon so they mirror in right-to-left layouts.
tooltipNexeraReactNodeTooltip text.
Shown on hover and keyboard focus and announced as the tool's description. Defaults to the aria-label, so every
icon-only tool shows its name to sighted users.
shortcutNexerastringKeyboard shortcut shown in the tooltip as <kbd>, written for people ("Ctrl+B", "⌘B"). It is a hint only: the editor
registers the shortcut. Also set aria-keyshortcuts so assistive technology announces it.
aria-keyshortcutsNexerastringThe same shortcut in ARIA syntax ("Control+B", "Meta+B"), set on the button as aria-keyshortcuts (WCAG 4.1.2).
tooltipPlacementNexera"top" | "bottom"Side the tooltip opens on; it flips when there is no room.
platformNexera"web" | "ios" | "android"Platform look: 32 px on web, 50 pt
on iOS, 52 dp on Android. Defaults to the enclosing toolbar, then the NexeraProvider.
idNexerastringDOM id of the button.
classNameNexerastringExtra classes, merged last so they win over the defaults.
styleNexeraCSSPropertiesInline style.
kindNexera"toggle" | "action"toggle (default): a toggle button with aria-pressed for formats that are on or off at the caret. action: a plain button for commands that have no on/off state (Undo, Redo, Insert image, Clear
formatting, Hide keyboard), so it never announces "not pressed".
A plain button: no aria-pressed. React to onPress.
isSelectedNexerabooleanWhether the format is on at the caret. Use with onChange.
defaultSelectedNexerabooleanWhether the format is on at first render (uncontrolled).
onChangeNexera(isSelected: boolean) => voidCalled with the new pressed state when the tool is toggled.
aria-labelNexerastringAccessible name ("Bold", "Bullet list"). Required unless aria-labelledby is set. Translate it.
Accessible name; aria-labelledby wins when both are set.
aria-labelledbyNexerastringId(s) of element(s) that name the tool; wins over aria-label.
Id(s) of visible element(s) that name the tool. Required unless aria-label is set.
aria-describedbyReact AriastringIdentifies the element (or elements) that describes the object.
isDisabledReact AriabooleanWhether the button is disabled.
onPressReact Aria(e: PressEvent) => voidHandler that is called when the press is released over the target.
onPressStartReact Aria(e: PressEvent) => voidHandler that is called when a press interaction starts.
onPressEndReact Aria(e: PressEvent) => voidHandler that is called when a press interaction ends, either over the target or when the pointer leaves the target.
onPressChangeReact Aria(isPressed: boolean) => voidHandler that is called when the press state changes.
onPressUpReact Aria(e: PressEvent) => voidHandler that is called when a press is released over the target, regardless of whether it started on the target or not.
autoFocusReact AriabooleanWhether the element should receive focus on render.
* 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
6 direct · 3 supporting- 1.1.1Non-text ContentLevel A · tested directly
- 1.3.1Info and RelationshipsLevel A · tested directly
- 1.4.1Use of ColorLevel A · supporting test
- 1.4.11Non-text ContrastLevel AA · supporting test
- 1.4.13Content on Hover or FocusLevel AA · tested directly
- 2.1.1KeyboardLevel A · tested directly
- 2.4.7Focus VisibleLevel AA · tested directly
- 2.5.8Target Size (Minimum)Level AA · supporting test
- 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.
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-disableddata-focus-visibledata-hovereddata-presseddata-selected
<RTETool className="data-disabled:opacity-90 shadow-sm" />Used in blocks
Related components
- CalendarDayOne day of a {@link CalendarMonth } grid, built on React Aria `CalendarCell`: a `gridcell` whose button is named by the full, localised date ("Wednesday, October 14, 2026"), with `aria-selected`, `aria-disabled` and the "today" and range descriptions read by screen readers (WCAG 1.3.1, 4.1.2).
- CalendarMonthA month calendar for picking a date or a date range: header with previous / next buttons and the localised month name, weekday row, and six weeks of `CalendarDay`s.
- CheckboxThe bare 18 px checkbox: one independent yes/no choice applied on submit, or a row selector in a table, list or tree.
- CheckboxCardA large checkbox option with an icon, a title and a description, for a few options that need explanation (notification channels, benefits).
- CheckboxFieldA checkbox with a label and optional helper text: the box (the same element as `Checkbox`) followed by the label and description, applied on submit.
- ColorInputA hex colour field with a swatch and a picker popover.
- ColorPickerPicks a colour.
- ColorTokenCardDocumentation card for one colour token: a 96 px colour sample with an "Aa" text sample, then the name, token path, hex value and contrast note as text.