Learn how the plugin registry works, where each button lives in the toolbar pipeline, and how to wire custom commands, actions, or React components without forking the core editor.
This section explains how to shape the toolbar around your product’s needs. Learn the four plugin archetypes, how to mix React icons with document commands, and how to run bespoke actions that inject HTML, data, or formatting. The examples and best practices below give you production-ready scaffolds plus guardrails for accessibility, error handling, and selection management.
Overview
The plugin system lets you compose the toolbar at runtime: pass an array of plugin objects and the editor renders them alongside the default formatting controls. Each plugin declares how it behaves (tag, command, or custom action), what UI it should render (icon + tooltip), and when it should be enabled (based on selection state).
- Declarative API: Provide plain objects—no need to subclass or patch the editor. Hot reload friendly.
- Toolbar integration: Plugins appear in deterministic slots so you can interleave them between default groups or render custom rows.
- Execution flexibility: Trigger DOM commands, run arbitrary JavaScript, or reuse pre-built plugin types for common tasks.
- Safety rails: When the editor is read-only or selection is empty, the plugin buttons automatically disable to prevent errors.
Plugin Types
1. HTML Tag Plugin
Wraps selected text with a custom HTML tag. This is ideal for semantic inline decorations (highlight, badge, custom spans) where you want predictable markup in the saved HTML string.
When to use: Apply consistent markup that your rendering pipeline or email templates already understand (e.g., wrap text in <mark> or <span class="mention">).
{
name: "highlight",
icon: <FaHighlighter />,
title: "Highlight Text",
tag: "mark"
}Properties:
name: Unique identifiericon: React component or stringtitle: Tooltip texttag: HTML tag name (e.g.,"mark","span","div")
The generated HTML still flows through DOMPurify sanitization. Make sure your tag is included in the whitelist if you need custom attributes or classes.
2. Command Plugin
Executes a document command (using document.execCommand). Think of this as a thin abstraction over the browser’s built-in formatting stack—perfect for headings, bold/italic toggles, or list insertion.
Limitations: Because execCommand is deprecated but still widely supported, always provide a graceful fallback (e.g., disable the button) for browsers that might drop support in the future. For deterministic output, consider the HTML Tag or Action plugin approach instead.
{
name: "heading2", // Unique plugin identifier
icon: "H2", // Button icon (text or React component)
title: "Heading 2", // Tooltip text
cmd: "formatBlock", // Document command to execute
arg: "h2" // Argument for the command
}Available Commands:
formatBlock– Format block element (h1, h2, p, etc.)bold– Make text bolditalic– Make text italicunderline– Underline textstrikeThrough– Strikethrough textjustifyLeft– Left alignjustifyCenter– Center alignjustifyRight– Right aligninsertUnorderedList– Insert bullet listinsertOrderedList– Insert numbered listcreateLink– Create linkremoveFormat– Remove formatting
3. Action Plugin
Executes a custom JavaScript function. Action plugins unlock bespoke workflows: insert snippets fetched from an API, open modals, track analytics, or transform the current selection with your own logic.
Tip: Actions receive the editor instance so you can read the HTML value, focus the editor, or trigger selection helpers before injecting custom content.
{
name: "timestamp", // Unique plugin identifier
icon: <FaClock />, // React icon component
title: "Insert Timestamp", // Tooltip text
action: (editor) => { // Custom function to execute
const now = new Date();
const timestamp = now.toLocaleString();
document.execCommand("insertText", false, timestamp); // Insert text at cursor
}
}4. Type Plugin
Uses built-in plugin types. These are prepackaged behaviors (timestamp, divider, lorem ipsum) that ship with the editor so you can enable common features without re-implementing them.
Extensibility: You can register your own types inside the editor configuration and then reference them by name across your app to keep plugin definitions DRY.
{
name: "timestamp", // Unique plugin identifier
icon: <FaClock />, // React icon component
title: "Insert Time", // Tooltip text
type: "timestamp" // Built-in plugin type
}Available Types:
"timestamp"– Inserts current timestamp
Complete Examples
The following scenarios show how to bundle multiple plugin archetypes together, manage state, and style the toolbar so custom buttons feel native. Use them as blueprints for your own editor presets.
Example 1: Multiple Plugins
import React, { useState } from 'react';
import Editor from '@webbycrown/react-advanced-richtext-editor';
// Import the default stylesheet - required for proper editor appearance
import '@webbycrown/react-advanced-richtext-editor/dist/styles.css';
// Import React icons
import { FaHighlighter, FaClock, FaCode } from 'react-icons/fa';
// Define multiple custom plugins
const customPlugins = [
{
name: "timestamp", // Plugin identifier
icon: <FaClock />, // Icon component
title: "Insert Timestamp", // Tooltip text
action: (editor) => { // Custom action function
const now = new Date();
document.execCommand("insertText", false, now.toLocaleString());
}
},
{
name: "highlight", // Plugin identifier
icon: <FaHighlighter />, // Icon component
title: "Highlight Text", // Tooltip text
tag: "mark" // Wrap selected text with <mark> tag
},
{
name: "codeBlock", // Plugin identifier
icon: <FaCode />, // Icon component
title: "Code Block", // Tooltip text
action: (editor) => { // Custom action to insert code block
document.execCommand("insertHTML", false, '<pre><code></code></pre>');
}
}
];
function MyEditor() {
// Store HTML content in state
const [content, setContent] = useState('');
return (
<Editor
value={content} // Current HTML content
onChange={setContent} // Update state when content changes
plugins={customPlugins} // Add custom plugins to toolbar
height={400} // Editor height in pixels
/>
);
}Get started with the React Advanced Rich Text Editor in just a few lines of code.
Type rich text here and manage the HTML string in your React state.
This is a quick example showing the default editor with all features enabled.
💡 Vite Tips:
- Vite automatically handles CSS imports – no additional configuration needed
- For production builds, Vite optimizes the editor bundle automatically
- If using TypeScript, the editor includes full type definitions
- Vite’s fast HMR means editor changes reflect instantly during development
This configuration mixes one action plugin, one tag plugin, and one action that injects HTML. Because plugins are plain objects, you can source them from JSON, feature flags, or user-specific toolsets.
Example 2: Custom Styling Plugin
Demonstrates how to reach into the DOM selection, wrap it with styled markup, and keep the experience accessible by falling back when no text is highlighted.
import React, { useState } from 'react';
import Editor from '@webbycrown/react-advanced-richtext-editor'; // Import the default bundle
// Import the default stylesheet - required for proper editor appearance
import '@webbycrown/react-advanced-richtext-editor/dist/styles.css';
const customStylePlugin = { // Custom style plugin
name: "customStyle", // Plugin identifier
icon: "🎨", // Icon component
title: "Custom Style", // Tooltip text
action: (editor) => { // Custom action to apply custom style
const selection = window.getSelection(); // Get selected text
if (selection.rangeCount > 0) {
const range = selection.getRangeAt(0); // Get selected text range
const span = document.createElement('span');
span.style.color = '#ff0000';
span.style.fontWeight = 'bold';
span.textContent = selection.toString();
range.deleteContents();
range.insertNode(span);
}
}
};
function CustomStyledEditor() {
const [content, setContent] = useState(''); // Store HTML content in state
return (
<Editor
value={content}
onChange={setContent}
plugins={[customStylePlugin]} // Add custom style plugin to toolbar
height={400}
/>
);
}Get started with the React Advanced Rich Text Editor in just a few lines of code.
Type rich text here and manage the HTML string in your React state.
This is a quick example showing the default editor with all features enabled.
💡 Vite Tips:
- Vite automatically handles CSS imports – no additional configuration needed
- For production builds, Vite optimizes the editor bundle automatically
- If using TypeScript, the editor includes full type definitions
- Vite’s fast HMR means editor changes reflect instantly during development
Plugin Best Practices
- Unique Names: Always use unique plugin names to avoid conflicts. Names double as analytics keys and React list keys, so duplicates can cause rendering bugs.
- Accessible Icons: Use meaningful icons that users can understand and pair them with
titletext so screen readers announce the action. - Descriptive Titles: Provide clear tooltip text plus optional hotkey hints (e.g., “Highlight (Ctrl+Shift+H)”) to improve discoverability.
- Error Handling: Wrap actions in try/catch blocks, especially when hitting APIs or manipulating the DOM. If something fails, surface a toast and leave the selection untouched.
- Selection Handling: Check if text is selected before applying formatting. Disable the button when nothing is highlighted to prevent confusing no-ops.
- Undo/Redo Safety: Fire actions through
document.execCommandor use the editor’s mutation helpers so the browser history stack stays intact.
Troubleshooting
Most plugin issues trace back to missing props, stale selections, or sanitization filters. Use the checklist below when something doesn’t behave as expected.
Plugin Not Appearing
- Check that the plugin array is properly passed to the
pluginsprop and that you are not mutating it in place (React needs a new array reference). - Verify that each plugin object has required properties (
name,icon,title). Missing names are silently ignored to prevent toolbar crashes. - Inspect browser console for prop-type warnings or sanitization errors indicating that a tag/attribute was stripped.
Plugin Not Working
- Ensure the action function is properly defined and bound. Arrow functions are recommended to keep
thisscope predictable. - Check that document commands are supported in your browser; Safari Tech Preview occasionally disables certain
execCommandcalls. - Verify selection handling in action plugins. Remember to call
editor.focus()before injecting HTML if your action opens a modal first. - Confirm the generated markup survives sanitization—if you inject custom attributes, extend the whitelist accordingly.