content/docs/(guides)/plugin-context.mdx
Plugin context is the object Plate passes to plugin configuration callbacks,
handlers, extensions, transforms, and render components. It gives you the
resolved editor, current plugin, node type, api, tf, and option helpers
without reaching through global state. Use it inside plugin-owned code; use
editor methods or React hooks when code runs outside a plugin callback.
PlatePluginContext extends the shared plugin context with a React
PlateEditor. The same helper names are available in headless Slate plugins,
but the editor type is SlateEditor.
| Property | Use for |
|---|---|
editor | The resolved editor instance. |
plugin | The resolved plugin configuration for the current plugin. |
type | The plugin node type, usually plugin.node.type. |
api | Editor API plus plugin-specific API methods. |
tf | Editor transforms plus plugin-specific transforms. |
getOption(key, ...args) | Read an option, selector, or 'state' from the current plugin. |
getOptions() | Read the full option state for the current plugin. |
setOption(key, value) | Update one option in the current plugin store. |
setOptions(optionsOrDraft) | Update multiple options or mutate a draft. |
Handlers receive context plus the event or payload for that handler. Use the context helpers instead of closing over editor state.
import { createPlatePlugin } from 'platejs/react';
export const CounterPlugin = createPlatePlugin({
key: 'counter',
options: {
count: 0,
enabled: true,
},
handlers: {
onKeyDown: ({ event, getOption, setOption, type }) => {
if (!getOption('enabled')) return;
if (event.key === '+') {
setOption('count', getOption('count') + 1);
console.info(`${type} count incremented`);
}
},
},
});
getOption and setOption are scoped to CounterPlugin in this example.
Configuration, extension, selector, API, transform, and editor override callbacks also receive plugin context.
import { createPlatePlugin } from 'platejs/react';
export const CounterPlugin = createPlatePlugin({
key: 'counter',
options: {
count: 0,
},
})
.extendSelectors(({ getOptions }) => ({
label: () => `Count: ${getOptions().count}`,
}))
.extendApi(({ getOption }) => ({
isEmpty: () => getOption('count') === 0,
}));
Selectors are readable through getOption and subscribable through
usePluginOption.
Use getEditorPlugin(editor, Plugin) when plugin-owned code needs another
plugin's context. The editor argument is required.
import { LinkPlugin } from '@platejs/link/react';
import { createPlatePlugin, getEditorPlugin } from 'platejs/react';
export const LinkAwarePlugin = createPlatePlugin({
key: 'linkAware',
handlers: {
onKeyDown: ({ editor, event }) => {
if (event.key !== 'Enter') return;
const link = getEditorPlugin(editor, LinkPlugin);
console.info(`Link node type: ${link.type}`);
},
},
});
Use this for cross-plugin reads. Keep cross-plugin writes rare; they couple two plugins tightly.
Use useEditorPlugin inside a component rendered under <Plate>. It returns
the same context plus the editor store.
import { useEditorPlugin, usePluginOption } from 'platejs/react';
import { CounterPlugin } from './counter-plugin';
export function CounterBadge() {
const { type } = useEditorPlugin(CounterPlugin);
const count = usePluginOption(CounterPlugin, 'count');
const label = usePluginOption(CounterPlugin, 'label');
return (
<span data-plugin-type={type}>
{label} ({count})
</span>
);
}
Use usePluginOptions when a component needs a derived value from several
options.
import { usePluginOptions } from 'platejs/react';
import { CounterPlugin } from './counter-plugin';
export function CounterStatus() {
const status = usePluginOptions(CounterPlugin, (state) =>
state.count === 0 ? 'empty' : 'active'
);
return <span>{status}</span>;
}
For code outside the nearest <Plate> provider, pass an editor explicitly with
useEditorPluginOption or useEditorPluginOptions.
Plugin options are stored per editor. Updating one editor's plugin options does not update another editor.
export const CounterPluginWithInitialCount = CounterPlugin.configure(
({ getOptions }) => ({
options: {
count: getOptions().count + 1,
},
})
);
setOptions accepts either a partial object or a draft callback.
import { getEditorPlugin, type PlateEditor } from 'platejs/react';
import { CounterPlugin } from './counter-plugin';
export function resetCounter(editor: PlateEditor) {
const { setOptions } = getEditorPlugin(editor, CounterPlugin);
setOptions({
count: 1,
});
setOptions((draft) => {
draft.count += 1;
});
}
Plate reports OPTION_UNDEFINED through the debug API when getOption,
setOption, or usePluginOption targets a missing option or selector.
| Helper | Scope | Notes |
|---|---|---|
getEditorPlugin(editor, plugin) | Any editor code. | Returns plugin context for the given editor and plugin. |
useEditorPlugin(plugin, id?) | React under <Plate>. | Returns plugin context plus store. |
usePluginOption(plugin, key, ...args) | React under <Plate>. | Subscribes to one option, selector, or 'state'. |
usePluginOptions(plugin, selector, options?) | React under <Plate>. | Subscribes to a selected value from the option state. |
useEditorPluginOption(editor, plugin, key, ...args) | React with explicit editor. | Use outside the closest editor provider. |
useEditorPluginOptions(editor, plugin, selector, options?) | React with explicit editor. | Explicit-editor variant of usePluginOptions. |
For plugin extension methods, see Plugin Methods. For plugin configuration, see Plugin.