RichTextEditor

A contenteditable-based rich text editor with a fully customisable toolbar. Always outputs clean, semantic HTML — including <p> blocks, headings, and lists. Toolbar tools are grouped automatically using Manhattan ButtonGroup styling. Keyboard shortcuts work on both Windows/Linux (Ctrl) and macOS (Cmd).

Tab key behaviour: Pressing Tab inside a list item creates a sub-list (numbered → lettered, bullets → open circles). Shift+Tab promotes a sub-item back up. Outside of a list, Tab inserts a visual indent (4 non-breaking spaces).

Toolbar dropdown keyboard navigation: Toolbar dropdowns (e.g. Text Format, Font Size) can be opened with Enter or Space when the trigger is focused, or opens and jumps straight to the first item. Use / to move between options, Enter/Space to select, and Esc to close without selecting. The Text Format dropdown shows a live style preview of each heading level.

Default Toolbar

The default toolbar includes bold, italic, underline, alignment, lists, heading level, font size, and text colour.

PHP

List Indentation (Tab / Shift+Tab)

Inside a list item, pressing Tab creates a nested sub-list. Shift+Tab promotes an item back to the parent level.
Sub-list styling is automatic: ordered lists use lettered sub-items (a, b, c…), and unordered lists use open-circle bullets. Outside a list, Tab inserts a visual indent.

  1. First item
  2. Second item
  3. Third item
  • Apples
  • Bananas
  • Cherries
PHP

Character Count with Limits

Enable a live character counter with ->showCharCount(). Set ->minChars() and/or ->maxChars() to enforce limits — the counter is automatically shown when limits are set. The count turns orange as you approach 90% of the maximum, and red when a limit is violated. An error message appears below the editor (using the same style as Validator errors), and the editor border turns red.

PHP

Custom Toolbar & Link Insertion

Pass an array of tool names to ->toolbar() to show exactly the tools you need. Use 'separator' to add a visual divider between groups. The 'link' tool opens a Manhattan-styled dialog (not the browser prompt) with a URL field and an "Open in new tab" checkbox.

PHP

Pre-populated Content

Pass existing HTML to ->value() to pre-load the editor. The same HTML can be displayed outside the editor using the m-richtext CSS class for consistent typography.

Welcome to Manhattan

This editor outputs clean semantic HTML that looks great everywhere.

  • Supports headings and lists
  • Works with custom text colours
  • Keyboard shortcuts on all platforms
PHP

Displaying Saved Content

Wrap stored rich-text HTML in <div class="m-richtext"> to apply the same consistent typography that appears inside the editor.

Article Title

This is a paragraph with bold text, italic text, and a link.

A Section Heading

More content follows, demonstrating how the m-richtext class applies consistent typography to any stored HTML when rendered outside of the editor.

  • First bullet point
  • Second bullet point with coloured text
  • Third point

And an ordered list:

  1. Step one
  2. Step two
  3. Step three
PHP

Read-only Mode

->readOnly() disables editing and dims the toolbar. Useful for preview panels or displaying content that should not be changed.

This content is read-only and cannot be edited.

PHP

JavaScript Events

Listen for editor events to react to content changes or focus state.

Events will appear here…
JS

Image Insertion — URL

Adding 'image' to the toolbar renders an Insert Image button. Clicking it opens a dialog where you can enter any image URL and optional alt text. No uploader configuration is required for URL-based insertion.

PHP

Image Insertion — File Upload & Paste

To enable file upload (via the Insert Image dialog) and/or paste-to-upload, configure an uploader endpoint using ->uploader($url, $stem). The endpoint receives a multipart/form-data POST with an image file field (and an optional stem text field) and must return { "url": "/path/to/saved/image.ext" }. Use ->allowPasteImages() to also allow users to paste raw image data (e.g. screenshots) directly into the editor. If pasting is attempted and no uploader is configured a toaster error is shown automatically.

PHP

Image Alignment & Resize

Clicking any image in the editor reveals a small alignment toolbar above it with three modes:

  • Leftfloat: left. Text in the same paragraph wraps to the right of the image.
  • Centredisplay: block; margin: auto (+ text-align: center on the parent paragraph as a fallback). The image sits on its own centred line with no text wrapping.
  • Rightfloat: right. Text in the same paragraph wraps to the left of the image.

Alignment is always available when the 'image' tool is in the toolbar; no extra option is needed. Paragraphs containing floated images are automatically cleared (via CSS overflow: hidden in .m-richtext and a ::after clearfix in the editor) so the float never bleeds into the next paragraph.

Enable ->allowImageResize() to also show 8-point drag handles around the selected image. Corner handles resize proportionally; edge handles scale on a single axis. The image's original natural dimensions are preserved in data-original-width / data-original-height attributes.

Click the image below to select it, then try each alignment button. Float left/right lets text wrap around the image; centre isolates it on its own line.

Sample image Text that wraps to the right when the image is float-left. Try clicking Right align to make this text move to the other side instead.

PHP

YouTube Video Embed

Adding 'youtube' to the toolbar renders an Embed YouTube Video button (). Clicking it opens a dialog where you can paste any YouTube URL — watch links, shortened youtu.be links, embed URLs, or even bare 11-character video IDs. A responsive 16:9 iframe wrapper (.m-rte-youtube-wrapper) is inserted at the cursor. Videos are always block-centred — they cannot be floated. Width is adjustable via drag handles; the embed stays centred regardless of width. Videos are served via youtube-nocookie.com for privacy. A Video Credit line is automatically fetched from the YouTube oEmbed API and appended below each embed, linking to the video's channel by name. The credit is part of the saved HTML output and is styled via .m-rte-youtube-credit.

PHP

Scrollable Input Area

By default the editor auto-extends its height to fit all content, which can push the rest of the page down. Calling ->scrollable() (optionally combined with ->maxHeight()) constrains the editing area and scrolls the content within it using Apple-style thin overlay scrollbars — the same style used by the image viewer thumbstrip. The editor still auto-grows up to the specified max-height and then scrolls; the toolbar and footer stay visible at all times.

This editor is capped at 280 px and scrolls internally once the content grows past that limit. Try typing several paragraphs to see the thin overlay scrollbar appear on hover.

The toolbar and character counter (if enabled) remain fixed outside the scrollable region, so they are always accessible.

PHP

PHP PHP Methods (Fluent)

Method / PropertyParametersDescription
$m->richTextEditor($id)stringCreate a RichTextEditor component.
->name($name)stringSet the hidden input's name attribute for form submission.
->value($html)stringPre-load the editor with HTML content.
->placeholder($text)stringPlaceholder text shown when the editor is empty.
->showCharCount()Show a live character count in the footer. Default: false.
->minChars($n)intMinimum character count required. Enables char counter automatically.
->maxChars($n)intMaximum character count allowed. Enables char counter automatically. Counter turns orange at 90%, red when exceeded.
->customColor($show)boolShow the custom colour input in the colour picker. Default: true.
->toolbar($tools)string[]Define which tools appear in the toolbar (see available tools below). Default: full toolbar.
->minHeight($px)intMinimum height of the editing area in pixels. Default: 200.
->maxHeight($px)intMaximum height of the editing area in pixels. When exceeded, the body scrolls. Default: none.
->scrollable()Enable Apple-style thin overlay scrollbars on the editing area. Best combined with ->maxHeight() to constrain the editor height; the scrollbar fades in on hover and disappears when idle. Default: false.
->readOnly()Disable editing and dim the toolbar. Default: false.
->uploader($url, $stem)string, string?Configure the image upload endpoint. The POST endpoint must return { "url": "…" }. Optional $stem is sent as a stem field to suggest a filename prefix.
->allowPasteImages()Allow pasted raw images (screenshots etc.) to be auto-uploaded via the uploader. Requires ->uploader(). Default: false.
->allowImageResize()Show 8-point drag handles when an image is selected, allowing the user to resize it. The image's original natural dimensions are stored in data-original-width / data-original-height attributes. Default: false.

PHP Toolbar Tools

Method / PropertyParametersDescription
'bold'Bold (Ctrl/Cmd+B).
'italic'Italic (Ctrl/Cmd+I).
'underline'Underline (Ctrl/Cmd+U).
'strikethrough'Strikethrough.
'align'Alignment group: left, centre, right, justify.
'orderedList'Numbered list. Use Tab/Shift+Tab to indent/promote items.
'bulletList'Bullet list. Use Tab/Shift+Tab to indent/promote items.
'heading'Block format dropdown: Normal / H1–H4.
'fontSize'Font size dropdown: Tiny → Huge.
'foreColor'Text colour picker (presets + custom).
'link'Insert / edit a hyperlink.
'image'Insert an image. Opens a dialog for URL entry and (if uploader configured) file upload.
'youtube'Embed a YouTube video. Opens a dialog where you paste any YouTube URL or video ID — a responsive 16:9 iframe is inserted.
'undo'Undo.
'redo'Redo.
'clearFormat'Remove all inline formatting.
'separator'A visual divider between tool groups.

JS JS Methods

Method / PropertyParametersDescription
m.richTextEditor(id)stringGet (or create) an editor instance.
rte.getValue()stringReturn the current HTML content.
rte.setValue(html)stringReplace the editor content programmatically.
rte.focus()Focus the editing area.
rte.execCommand(cmd, val)string, string?Execute a toolbar command programmatically.
rte.insertImage(url, alt)string, string?Insert an image at the current cursor position.

EVENT Events

Event NameDetailDescription
m:rte:change{ value: string }Fired on the container whenever content changes. detail.value is the current HTML.
m:rte:focus{}Fired when the editing area receives focus.
m:rte:blur{}Fired when the editing area loses focus.
m:rte:error{ message: string }Fired when an error occurs (e.g. paste attempted without uploader configured).
m:rte:upload:start{}Fired when an image upload begins.
m:rte:upload:end{ success: bool, url: string|null, error: string|null }Fired when an upload completes. detail.url is the image URL on success.