Back to Plate

Plugin Context

content/docs/(guides)/plugin-context.mdx

53.0.86.2 KB
Original Source

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.

Context Shape

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.

PropertyUse for
editorThe resolved editor instance.
pluginThe resolved plugin configuration for the current plugin.
typeThe plugin node type, usually plugin.node.type.
apiEditor API plus plugin-specific API methods.
tfEditor 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.

Plugin Methods

Handlers receive context plus the event or payload for that handler. Use the context helpers instead of closing over editor state.

ts
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.

Extension Callbacks

Configuration, extension, selector, API, transform, and editor override callbacks also receive plugin context.

ts
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.

Another Plugin

Use getEditorPlugin(editor, Plugin) when plugin-owned code needs another plugin's context. The editor argument is required.

ts
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.

React Components

Use useEditorPlugin inside a component rendered under <Plate>. It returns the same context plus the editor store.

tsx
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.

tsx
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.

Option State

Plugin options are stored per editor. Updating one editor's plugin options does not update another editor.

ts
export const CounterPluginWithInitialCount = CounterPlugin.configure(
  ({ getOptions }) => ({
    options: {
      count: getOptions().count + 1,
    },
  })
);

setOptions accepts either a partial object or a draft callback.

ts
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.

API Reference

HelperScopeNotes
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.