Overview
Plugins are the building blocks of Yoopta Editor. Each plugin represents a specific type of content block (paragraph, heading, image, etc.) and defines how that content is rendered and behaves.
Every plugin in Yoopta Editor is built using the YooptaPlugin class, which provides a consistent API for defining block types, handling events, managing state, and extending functionality.
Plugin Architecture
YooptaPlugin Class
All plugins are created using the YooptaPlugin class constructor:
import { YooptaPlugin } from '@yoopta/editor';
const MyPlugin = new YooptaPlugin({
type: 'MyPlugin',
elements: {
// Plugin elements definition
},
options: {
// Plugin options
},
commands: {
// Plugin commands
},
events: {
// Event handlers
},
lifecycle: {
// Lifecycle hooks
},
parsers: {
// HTML, Markdown, Email parsers
},
extensions: {
// Slate extensions
},
});
Plugin Structure
Each plugin configuration consists of:
Unique identifier for the plugin (e.g., ‘Paragraph’, ‘Image’, ‘Code’)
elements
object | JSX.Element
required
Defines the structure and rendering of plugin elements. Can be an object or JSX syntax
Configuration options including display settings and shortcuts
Methods for manipulating plugin content programmatically
Event handlers for keyboard, mouse, and other DOM events
Hooks that run at specific points in the plugin lifecycle
Serialization and deserialization for HTML, Markdown, and Email
Slate editor extensions for custom behavior
Available Plugins
Yoopta Editor provides 20+ built-in plugins:
| Category | Plugins |
|---|
| Text | Paragraph, Headings (H1, H2, H3), Blockquote |
| Lists | BulletedList, NumberedList, TodoList |
| Media | Image, Video, File, Embed |
| Layout | Table, Accordion, Tabs, Steps, Carousel, Callout, Divider |
| Code | Code, CodeGroup |
| Inline | Link, Mention, Emoji |
| Navigation | TableOfContents |
Using Plugins
Plugins are passed to createYooptaEditor, not to <YooptaEditor>. The component only receives the editor instance.
Basic Usage
import { useMemo } from 'react';
import YooptaEditor, { createYooptaEditor } from '@yoopta/editor';
import Paragraph from '@yoopta/paragraph';
import Blockquote from '@yoopta/blockquote';
import Code from '@yoopta/code';
const plugins = [Paragraph, Blockquote, Code];
export default function Editor() {
const editor = useMemo(
() => createYooptaEditor({ plugins, marks: [] }),
[],
);
return (
<YooptaEditor
editor={editor}
onChange={(value) => {}}
placeholder="Type / to open menu"
/>
);
}
Headings Plugin
The Headings plugin exports multiple heading types:
import Headings from '@yoopta/headings';
const plugins = [
Headings.HeadingOne, // H1
Headings.HeadingTwo, // H2
Headings.HeadingThree, // H3
];
Lists Plugin
The Lists plugin exports multiple list types:
import Lists from '@yoopta/lists';
const plugins = [Lists.BulletedList, Lists.NumberedList, Lists.TodoList];
Plugin Configuration
Most plugins accept configuration options when you call plugin.extend({ options: { ... } }). Pass the resulting plugins array to createYooptaEditor({ plugins, marks }), not to <YooptaEditor>.
Image Plugin
import Image from '@yoopta/image';
const plugins = [
Image.extend({
options: {
async onUpload(file) {
// Custom upload logic
const url = await uploadToServer(file);
return { src: url };
},
maxWidth: 1000,
minWidth: 100,
},
}),
];
Code Plugin
import Code from '@yoopta/code';
const plugins = [
Code.extend({
options: {
theme: 'github-dark',
language: 'javascript',
languages: ['javascript', 'typescript', 'python', 'rust'],
},
}),
];
Video Plugin
import Video from '@yoopta/video';
const plugins = [
Video.extend({
options: {
async onUpload(file) {
const url = await uploadVideo(file);
return { src: url, provider: 'custom' };
},
maxSize: 100 * 1024 * 1024, // 100MB
accept: 'video/*',
},
}),
];
Plugin Methods
extend()
Extend a plugin with custom options or elements (UI):
const CustomParagraph = Paragraph.extend({
options: {
display: { title: 'Text', description: 'Plain text' },
shortcuts: ['p'],
},
elements: {
paragraph: {
render: (props) => (
<p className="my-paragraph" {...props.attributes}>
{props.children}
</p>
),
},
},
});
Defining Elements
Elements can be defined in two ways:
1. Object Syntax:
const MyPlugin = new YooptaPlugin({
type: 'MyPlugin',
elements: {
'my-element': {
render: (props) => <div {...props.attributes}>{props.children}</div>,
props: {
/* default props */
},
nodeType: 'block', // or 'void' or 'inline'
},
},
});
2. JSX Syntax (Recommended):
const MyPlugin = new YooptaPlugin({
type: 'MyPlugin',
elements: (
<my-container render={MyContainer}>
<my-item render={MyItem} />
</my-container>
),
});
Element Properties
React component that renders the element
Default props for the element
nodeType
'block' | 'void' | 'inline'
Type of Slate node. Default is ‘block’
Placeholder text shown when this element is focused and empty. Set via .extend():HeadingOne.extend({
elements: {
'heading-one': { placeholder: 'Heading 1' },
},
})
Element-level placeholders take priority over the editor-level placeholder prop. For nested plugins (Steps, Accordion), each element can have its own placeholder. See Editor Placeholders for the full guide.
Custom elements (renders)
Override default rendering by extending with custom elements:
import Paragraph from '@yoopta/paragraph';
const CustomParagraph = Paragraph.extend({
elements: {
paragraph: {
render: (props) => (
<p className="custom-paragraph" {...props.attributes}>
{props.children}
</p>
),
},
},
});
Plugin Events
Handle plugin-specific events:
const CustomImage = Image.extend({
events: {
onPaste: (editor, slate, options) => {
return (event) => {
console.log('onPaste', event);
};
},
onKeyDown: (editor, slate, options) => {
return (event) => {
console.log('onKeyDown', event);
};
},
},
});
Plugin shortcuts
Configure slash-menu shortcuts via options.shortcuts (array of strings). Plugin-specific hotkeys are defined in the plugin’s events.onKeyDown or in options. Use Blocks.toggleBlock(editor, { type: 'Code' }) for programmatic block type change.
Common Plugin Options
Most plugins support these common options:
Display configuration - title - Display name in UI - description - Plugin description - icon
Keyboard shortcuts to trigger the plugin
Slate-specific configuration
Plugin Loading Order
The order of plugins in the array affects:
- Menu display order
- Transform priority
- Shortcut precedence
const plugins = [
Paragraph, // Shows first in menus
Headings.HeadingOne,
Headings.HeadingTwo,
// ...
Divider, // Shows last in menus
];
Best Practices
- Always include the Paragraph plugin — it’s the default block type
- Define your
plugins array outside the component to prevent recreation on every render
- Use
extend() to customize plugins instead of modifying plugin objects directly
- For plugins with async operations (uploads), always handle errors and loading states
Lifecycle Hooks
Lifecycle hooks allow you to run code at specific points in a block’s lifecycle:
const MyPlugin = new YooptaPlugin({
type: 'MyPlugin',
lifecycle: {
beforeCreate: (editor) => {
// Return initial block structure
return editor.y('my-element', {
children: [editor.y.text('Initial content')],
});
},
onCreate: (editor, block) => {
// Run after block is created
},
onDestroy: (editor, block) => {
// Cleanup before block is destroyed
},
},
});
Available Lifecycle Hooks
beforeCreate
(editor: YooEditor) => SlateElement
Called before a block is created. Return the initial structure
onCreate
(editor: YooEditor, block: YooptaBlockData) => void
Called after a block is created
onDestroy
(editor: YooEditor, block: YooptaBlockData) => void
Called before a block is destroyed
Parsers
Parsers handle serialization and deserialization of content:
const MyPlugin = new YooptaPlugin({
type: 'MyPlugin',
parsers: {
html: {
deserialize: {
nodeNames: ['DIV'],
parse: (el) => {
// Convert HTML element to Slate node
return {
type: 'my-element',
children: [{ text: el.textContent }],
};
},
},
serialize: (element, text, blockMeta) => {
// Convert Slate node to HTML string
return `<div>${text}</div>`;
},
},
markdown: {
serialize: (element, text) => {
// Convert to Markdown
return `${text}\n`;
},
},
email: {
serialize: (element, text) => {
// Convert to email-compatible HTML
return `<table><tr><td>${text}</td></tr></table>`;
},
},
},
});
Next Steps