content/docs/(guides)/plugin-methods.mdx
Plugin methods return new plugin instances, so you can keep a base plugin stable and derive app-specific behavior from it. Use .configure() for existing fields, .extend*() for typed additions, and .overrideEditor() only when wrapping editor APIs or transforms that already exist. This guide maps each method to the runtime surface it changes.
| Method | Use it for | Writes to |
|---|---|---|
.configure() | Change existing plugin fields without widening the public type. | The current plugin. |
.configurePlugin() | Change an existing nested plugin. | A child plugin already present in plugins. |
.extend() | Add typed options, handlers, renderers, rules, or runtime hooks. | The current plugin. |
.extendPlugin() | Extend a nested plugin, or add a keyed nested plugin when missing. | A child plugin under plugins. |
.extendSelectors() | Add computed option selectors. | getOption() and usePluginOption(). |
.extendApi() | Add plugin-specific API methods. | editor.api[plugin.key]. |
.extendEditorApi() | Add editor-wide API methods. | editor.api. |
.extendTransforms() | Add plugin-specific transforms. | editor.tf[plugin.key]. |
.extendEditorTransforms() | Add editor-wide transforms. | editor.tf. |
.overrideEditor() | Wrap existing editor API or transform methods. | editor.api and editor.tf. |
.withComponent() | Attach a node component to a plugin. | plugin.node.component and plugin.render.node. |
.clone() | Copy a plugin definition. | A new plugin object. |
Plugin method callbacks receive the same context described in Plugin Context: editor, plugin, api, tf, getOption, getOptions, setOption, setOptions, and type.
Use .configure() when the plugin already has the field and you only need to change its value.
import { H1Plugin } from '@platejs/basic-nodes/react';
export const AppH1Plugin = H1Plugin.configure({
shortcuts: {
toggle: { keys: 'mod+alt+1' },
},
});
Function configs run when the plugin resolves inside an editor, so they can read the current plugin options.
import { NavigationFeedbackPlugin } from 'platejs/react';
const LongerFlashPlugin = NavigationFeedbackPlugin.configure(
({ getOption }) => ({
options: {
duration: getOption('duration') + 400,
},
})
);
Object configs are merged with the plugin through Plate's plugin merge rules: objects merge deeply, arrays are replaced, and options are shallow merged.
Use .configurePlugin() when a parent plugin owns a child plugin and you want to adjust that child without replacing the whole parent.
import { createPlatePlugin } from 'platejs/react';
const CellPlugin = createPlatePlugin({
key: 'cell',
options: {
padding: 12,
},
});
export const GridPlugin = createPlatePlugin({
key: 'grid',
plugins: [CellPlugin],
}).configurePlugin(CellPlugin, {
options: {
padding: 8,
},
});
.configurePlugin() searches nested plugins recursively. If the target plugin is not found, Plate leaves the parent unchanged.
Use .extendPlugin() when the child needs new typed behavior.
export const GridWithCellShortcutPlugin = GridPlugin.extendPlugin(CellPlugin, {
shortcuts: {
insertBelow: {
keys: 'mod+enter',
handler: ({ event }) => {
event.preventDefault();
return true;
},
},
},
});
Use .extend() for broad plugin additions. Object extensions merge immediately; function extensions run during plugin resolution with the current editor context.
import { createPlatePlugin } from 'platejs/react';
export const MentionPlugin = createPlatePlugin({
key: 'mention',
node: {
isElement: true,
isInline: true,
},
}).extend(({ editor }) => ({
handlers: {
onKeyDown: ({ event }) => {
if (event.key === 'Escape') {
editor.tf.deselect();
event.preventDefault();
}
},
},
options: {
trigger: '@',
},
}));
Use .extend() when one extension naturally touches several plugin fields. Use the narrower methods below when the addition is specifically an API method, transform, selector, or editor override.
Use .extendSelectors() for derived option values that components can subscribe to. Selectors are available through getOption() and React hooks such as usePluginOption().
import { type PluginConfig } from 'platejs';
import { createTPlatePlugin, usePluginOption } from 'platejs/react';
type CounterOptions = {
value: number;
};
type CounterSelectors = {
doubled: (factor: number) => number;
isEven: () => boolean;
};
type CounterConfig = PluginConfig<
'counter',
CounterOptions,
{},
{},
CounterSelectors
>;
export const CounterPlugin = createTPlatePlugin<CounterConfig>({
key: 'counter',
options: {
value: 1,
},
}).extendSelectors<CounterSelectors>(({ getOptions }) => ({
doubled: (factor) => getOptions().value * factor,
isEven: () => getOptions().value % 2 === 0,
}));
export function CounterValue() {
const doubled = usePluginOption(CounterPlugin, 'doubled', 2);
const isEven = usePluginOption(CounterPlugin, 'isEven');
const value = usePluginOption(CounterPlugin, 'value');
return (
<span>
{value} / {doubled} / {isEven ? 'even' : 'odd'}
</span>
);
}
Selectors are the right place for derived state. Use .extendApi() when the method is a query or utility that should not subscribe React components to option changes.
Use API methods for reads and utilities. Use transforms for operations that mutate editor state.
| Method | Access path | Typical use |
|---|---|---|
.extendApi() | editor.api.counter.isEmpty() | Plugin-specific query or utility. |
.extendEditorApi() | editor.api.counterLabel() | Editor-wide query or utility. |
.extendTransforms() | editor.tf.counter.increment() | Plugin-specific mutation. |
.extendEditorTransforms() | editor.tf.resetCounter() | Editor-wide mutation. |
import { type PluginConfig } from 'platejs';
import { createTPlatePlugin } from 'platejs/react';
type CounterOptions = {
value: number;
};
type CounterPluginApi = {
isEmpty: () => boolean;
};
type CounterEditorApi = {
counterLabel: () => string;
};
type CounterPluginTransforms = {
increment: () => void;
};
type CounterEditorTransforms = {
resetCounter: () => void;
};
type CounterConfig = PluginConfig<'counter', CounterOptions>;
export const CounterPlugin = createTPlatePlugin<CounterConfig>({
key: 'counter',
options: {
value: 0,
},
})
.extendApi<CounterPluginApi>(({ getOption }) => ({
isEmpty: () => getOption('value') === 0,
}))
.extendEditorApi<CounterEditorApi>(({ getOption }) => ({
counterLabel: () => `Count: ${getOption('value')}`,
}))
.extendTransforms<CounterPluginTransforms>(({ getOption, setOption }) => ({
increment: () => setOption('value', getOption('value') + 1),
}))
.extendEditorTransforms<CounterEditorTransforms>(({ setOption }) => ({
resetCounter: () => setOption('value', 0),
}));
After the plugin resolves in an editor, call those methods from their resolved surfaces.
editor.api.counter.isEmpty();
editor.api.counterLabel();
editor.tf.counter.increment();
editor.tf.resetCounter();
editor.tf is the short alias for editor.transforms; both access the same transform tree.
Use .overrideEditor() when you need to wrap existing editor API or transform methods and keep access to the original method.
import { createPlatePlugin } from 'platejs/react';
export const LimitExclamationPlugin = createPlatePlugin({
key: 'limitExclamation',
}).overrideEditor(({ tf: { insertText } }) => ({
transforms: {
insertText(text, options) {
insertText(text === '!' ? '.' : text, options);
},
},
}));
The callback can override API methods, transform methods, or both.
const TrimStringPlugin = createPlatePlugin({
key: 'trimString',
}).overrideEditor(({ api: { string } }) => ({
api: {
string(at, options) {
return string(at, options).trim();
},
},
}));
Keep new editor methods in .extendEditorApi() or .extendEditorTransforms(). .overrideEditor() is for changing behavior while preserving the original call path.
Use .withComponent() to bind a Plate UI node component to a plugin. It writes both node.component and render.node, which keeps the component available to the editor renderer and plugin metadata.
import { ParagraphPlugin } from 'platejs/react';
import { ParagraphElement } from '@/components/ui/paragraph-node';
export const AppParagraphPlugin =
ParagraphPlugin.withComponent(ParagraphElement);
Use .withComponent() for the common one-component case. Use .extend() when the same plugin also needs render wrappers, handlers, options, or rules.
Use toPlatePlugin() when you have a headless Slate plugin and need to add React-only fields such as render, handlers, or useHooks.
import { createTSlatePlugin } from 'platejs';
import { toPlatePlugin } from 'platejs/react';
import { MentionElement } from '@/components/ui/mention-node';
const BaseMentionPlugin = createTSlatePlugin({
key: 'mention',
node: {
isElement: true,
isInline: true,
},
});
export const MentionPlugin = toPlatePlugin(BaseMentionPlugin, {
render: {
node: MentionElement,
},
});
toPlatePlugin() wraps the same plugin methods, so a converted plugin can still use .configure(), .extend(), .extendApi(), .overrideEditor(), and .withComponent().
| Method | Accepts | Resolution behavior |
|---|---|---|
.configure(config) | Object or callback returning a partial plugin config. | Stores one configuration callback and applies it before function extensions. |
.configurePlugin(plugin, config) | Target plugin and object or callback config. | Recursively configures an existing nested plugin; missing target is ignored. |
.extend(config) | Object or callback returning a partial plugin config. | Object configs merge immediately; callback configs run during plugin resolution. |
.extendPlugin(plugin, config) | Target plugin and object or callback extension. | Recursively extends a nested plugin; missing target is added by key. |
.extendSelectors(callback) | Callback returning selector functions. | Extends the plugin option store selectors. |
.extendApi(callback) | Callback returning functions. | Merges into editor.api[plugin.key] and plugin.api[plugin.key]. |
.extendEditorApi(callback) | Callback returning functions or one-level nested function objects. | Merges into editor.api and plugin.api. |
.extendTransforms(callback) | Callback returning functions. | Merges into editor.tf[plugin.key] and plugin.transforms[plugin.key]. |
.extendEditorTransforms(callback) | Callback returning functions or one-level nested function objects. | Merges into editor.tf and plugin.transforms. |
.overrideEditor(callback) | Callback returning { api?, transforms? }. | Deep-merges overrides into editor and plugin API/transform objects. |
.withComponent(component) | A node component. | Sets node.component and render.node. |
.clone() | No arguments. | Returns a merged copy of the plugin definition. |
Done. Use configuration for existing fields, extension methods for new typed surface, and overrides only when the editor method already exists.