Editor workflow and responsive UI
The editor keeps common writing actions close to the content while preserving the active selection when a menu, toolbar control, or dialog is opened.
Undo and redo
History is available through toolbar buttons, Edit-menu actions, keyboard shortcuts, and the public undo() and redo() methods. Undo and redo controls reflect whether a matching history step is currently available.
const editorConfig: EditorInit = {
toolbar: 'undo redo | bold italic',
menubar: ['edit'],
};Use Ctrl+Z or Cmd+Z to undo. Redo uses Ctrl+Y, Cmd+Shift+Z, or the toolbar action.
Clipboard and paste
The Edit menu provides Cut, Copy, Paste, and Paste as plain text. Cut and copy require a non-collapsed selection inside the editor. Paste uses the browser clipboard when permission is available and falls back to content copied through the editor during the current page session.
Normal paste handling:
- Preserves allowlisted headings, lists, links, images, tables, and inline formatting.
- Sanitizes pasted HTML before insertion when
sanitizeis enabled. - Converts plain-text line breaks to
<br>elements. - Uploads pasted image files when
pasteImages,automaticUploads, and an uploader are configured. - Never throws into the consumer application when clipboard permission is denied.
Browsers may reject programmatic clipboard reads unless the action follows a user gesture or the page has permission. In that case, use the operating system paste shortcut directly in the editable area.
Keyboard shortcuts
| Action | Windows and Linux | macOS |
|---|---|---|
| Bold | Ctrl+B | Cmd+B |
| Italic | Ctrl+I | Cmd+I |
| Underline | Ctrl+U | Cmd+U |
| Strikethrough | Ctrl+Shift+X | Cmd+Shift+X |
| Link dialog | Ctrl+K | Cmd+K |
| Undo | Ctrl+Z | Cmd+Z |
| Redo | Ctrl+Y / Ctrl+Shift+Z | Cmd+Y / Cmd+Shift+Z |
| Find and replace | Ctrl+F | Cmd+F |
| Preview | Ctrl+Shift+P | Cmd+Shift+P |
| Shortcut help | Alt+0 | Alt+0 |
Mention and merge-tag dropdowns handle ArrowUp, ArrowDown, Home, End, Enter, Tab, and Escape. Inside a table, Tab and Shift+Tab move between cells.
While the editor is read-only, only shortcuts for actions that do not change content (such as Preview) run. Other shortcuts are left to the browser, so Ctrl+F / Cmd+F opens the browser's own find bar instead of being blocked.
Responsive toolbar and menus
The toolbar measures its available width with ResizeObserver. Complete toolbar groups that no longer fit move behind the More (…) button instead of overflowing the editor. It recalculates when the editor or its parent changes size, or when the window is resized, so the behavior also works when a narrow editor is placed inside a wide desktop viewport.
Clicking More adds the hidden groups as a second toolbar row directly below the primary row. It is not a floating popup. The expanded row:
- Uses the same buttons, selects, group separators, active states, and disabled states as the primary toolbar.
- Remains open while the user clicks or continues typing in the editable canvas.
- Closes when More is toggled again or the user interacts outside the editor toolbar/content area.
- Opens alignment, line-height, color, list-style, and case-change popovers from the selected control as usual.
The menubar responds to the editor container width rather than only the viewport width. Menu entries wrap inside the editor and keep their text labels, so every menu stays readable in narrow editors. The editor itself can shrink below the previous 260px minimum without forcing horizontal page overflow.
Mobile screens
On screens 680px wide or narrower, the menubar and the toolbar each become a single row that scrolls sideways. Every toolbar group is shown, so the More button and its second row are not used. Swiping one bar sideways does not scroll the page.
Dropdowns and popovers
Menubar dropdowns and toolbar popovers (text and background color, alignment, list styles, line height, and change case) are positioned next to the button that opened them:
- They open directly below the button and stay inside the screen.
- They open above the button when there is not enough room below.
- They follow the button when the toolbar or menubar is scrolled sideways.
- Nested submenus open to the left, or shift back over their parent menu, when they would go off screen.
Menus close on outside interaction or Escape and support arrow-key navigation. Active formatting uses selected states, while commands that cannot run are disabled.
Toolbar icons use dedicated visual symbols for numbered lists, clear formatting, links, line height, checklists, and both indent directions. Dropdown groups show the currently relevant icon, including the active alignment icon.
Dialogs
While a dialog (Link, Table, Emoji, Source code, Preview, and so on) is open, the page behind it does not scroll. Long lists inside a dialog, such as the emoji grid, still scroll on their own. Page scrolling comes back when the last open dialog closes, even when several editors on the same page have dialogs open.
Status bar and word count
With statusbar: true, the bottom bar shows:
- The current element path, such as
p,ul > li, ortable > tbody > tr > td. - Current document word and character counts. Words in separate paragraphs, headings, or list items are counted separately, so
<p>one</p><p>two</p>is two words. - The configurable
helpShortcutText. - A resize handle when
resizeis enabled.
Tools → Word count opens detailed document and selection statistics for words, characters with spaces, and characters without spaces.
const editorConfig: EditorInit = {
statusbar: true,
helpShortcutText: 'Press Alt+0 for keyboard shortcuts',
};Editor resizing
Vertical resizing is enabled by default. Numeric dimensions are pixels, and CSS length strings are also accepted.
import { useState } from 'react';
import { Editor, type EditorInit } from '@erag/text-editor-react';
const editorConfig: EditorInit = {
height: 420,
minHeight: 240,
maxHeight: 720,
resize: true,
};
function handleResize({ height }: { height: number }): void {
console.log('Editor height:', height);
}
export default function ResizableEditor() {
const [content, setContent] = useState('');
return (
<Editor
value={content}
onChange={setContent}
init={editorConfig}
onResize={handleResize}
/>
);
}The resize handle clamps the result between minHeight and maxHeight and calls onResize with { height } while the user drags it.
Print only editor content
File → Print creates a temporary print-only copy of the sanitized editor content, opens the browser print dialog, and removes the temporary copy after printing. The surrounding application page and editor controls are excluded. The Preview dialog also provides a Print action.
Printing still follows the browser's print settings, available fonts, and page margins.