{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "plugin-methods-docs",
  "title": "Plugin Methods",
  "description": "Configure, extend, and override Plate plugins.",
  "files": [
    {
      "path": "../../content/docs/(guides)/plugin-methods.mdx",
      "content": "---\ntitle: Plugin Methods\ndescription: Configure, extend, and override Plate plugins.\n---\n\nPlugin 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.\n\n## On This Page\n\n- [Method Map](#method-map)\n- [Configure Existing Fields](#configure-existing-fields)\n- [Configure Nested Plugins](#configure-nested-plugins)\n- [Extend The Plugin](#extend-the-plugin)\n- [Selectors](#selectors)\n- [API And Transforms](#api-and-transforms)\n- [Override Editor Methods](#override-editor-methods)\n- [Components](#components)\n- [Convert Slate Plugins](#convert-slate-plugins)\n- [API Reference](#api-reference)\n\n## Method Map\n\n| Method | Use it for | Writes to |\n| --- | --- | --- |\n| `.configure()` | Change existing plugin fields without widening the public type. | The current plugin. |\n| `.configurePlugin()` | Change an existing nested plugin. | A child plugin already present in `plugins`. |\n| `.extend()` | Add typed options, handlers, renderers, rules, or runtime hooks. | The current plugin. |\n| `.extendPlugin()` | Extend a nested plugin, or add a keyed nested plugin when missing. | A child plugin under `plugins`. |\n| `.extendSelectors()` | Add computed option selectors. | `getOption()` and `usePluginOption()`. |\n| `.extendApi()` | Add plugin-specific API methods. | `editor.api[plugin.key]`. |\n| `.extendEditorApi()` | Add editor-wide API methods. | `editor.api`. |\n| `.extendTransforms()` | Add plugin-specific transforms. | `editor.tf[plugin.key]`. |\n| `.extendEditorTransforms()` | Add editor-wide transforms. | `editor.tf`. |\n| `.overrideEditor()` | Wrap existing editor API or transform methods. | `editor.api` and `editor.tf`. |\n| `.withComponent()` | Attach a node component to a plugin. | `plugin.node.component` and `plugin.render.node`. |\n| `.clone()` | Copy a plugin definition. | A new plugin object. |\n\nPlugin method callbacks receive the same context described in [Plugin Context](/docs/plugin-context): `editor`, `plugin`, `api`, `tf`, `getOption`, `getOptions`, `setOption`, `setOptions`, and `type`.\n\n## Configure Existing Fields\n\nUse `.configure()` when the plugin already has the field and you only need to change its value.\n\n```tsx title=\"plugins.tsx\" showLineNumbers\nimport { H1Plugin } from '@platejs/basic-nodes/react';\n\nexport const AppH1Plugin = H1Plugin.configure({\n  shortcuts: {\n    toggle: { keys: 'mod+alt+1' },\n  },\n});\n```\n\nFunction configs run when the plugin resolves inside an editor, so they can read the current plugin options.\n\n```tsx title=\"plugins.tsx\" showLineNumbers\nimport { NavigationFeedbackPlugin } from 'platejs/react';\n\nconst LongerFlashPlugin = NavigationFeedbackPlugin.configure(\n  ({ getOption }) => ({\n    options: {\n      duration: getOption('duration') + 400,\n    },\n  })\n);\n```\n\nObject configs are merged with the plugin through Plate's plugin merge rules: objects merge deeply, arrays are replaced, and `options` are shallow merged.\n\n<Callout type=\"info\" title=\"Configure does not widen types\">\n  `.configure()` is for existing plugin fields. If you need TypeScript to know\n  about a new option, API method, transform, selector, handler, or renderer,\n  use `.extend()` or the narrower `.extend*()` method.\n</Callout>\n\n## Configure Nested Plugins\n\nUse `.configurePlugin()` when a parent plugin owns a child plugin and you want to adjust that child without replacing the whole parent.\n\n```tsx title=\"plugins.tsx\" showLineNumbers\nimport { createPlatePlugin } from 'platejs/react';\n\nconst CellPlugin = createPlatePlugin({\n  key: 'cell',\n  options: {\n    padding: 12,\n  },\n});\n\nexport const GridPlugin = createPlatePlugin({\n  key: 'grid',\n  plugins: [CellPlugin],\n}).configurePlugin(CellPlugin, {\n  options: {\n    padding: 8,\n  },\n});\n```\n\n`.configurePlugin()` searches nested `plugins` recursively. If the target plugin is not found, Plate leaves the parent unchanged.\n\nUse `.extendPlugin()` when the child needs new typed behavior.\n\n```tsx title=\"plugins.tsx\" showLineNumbers\nexport const GridWithCellShortcutPlugin = GridPlugin.extendPlugin(CellPlugin, {\n  shortcuts: {\n    insertBelow: {\n      keys: 'mod+enter',\n      handler: ({ event }) => {\n        event.preventDefault();\n\n        return true;\n      },\n    },\n  },\n});\n```\n\n<Callout type=\"note\" title=\"Missing nested plugins\">\n  `.configurePlugin()` does not add a missing child. `.extendPlugin()` does:\n  when the target key is not found, Plate adds a keyed plugin at the top level\n  of the parent's `plugins` array and applies the extension there.\n</Callout>\n\n## Extend The Plugin\n\nUse `.extend()` for broad plugin additions. Object extensions merge immediately; function extensions run during plugin resolution with the current editor context.\n\n```tsx title=\"plugins.tsx\" showLineNumbers\nimport { createPlatePlugin } from 'platejs/react';\n\nexport const MentionPlugin = createPlatePlugin({\n  key: 'mention',\n  node: {\n    isElement: true,\n    isInline: true,\n  },\n}).extend(({ editor }) => ({\n  handlers: {\n    onKeyDown: ({ event }) => {\n      if (event.key === 'Escape') {\n        editor.tf.deselect();\n        event.preventDefault();\n      }\n    },\n  },\n  options: {\n    trigger: '@',\n  },\n}));\n```\n\nUse `.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.\n\n## Selectors\n\nUse `.extendSelectors()` for derived option values that components can subscribe to. Selectors are available through `getOption()` and React hooks such as `usePluginOption()`.\n\n```tsx title=\"counter-plugin.tsx\" showLineNumbers\nimport { type PluginConfig } from 'platejs';\nimport { createTPlatePlugin, usePluginOption } from 'platejs/react';\n\ntype CounterOptions = {\n  value: number;\n};\n\ntype CounterSelectors = {\n  doubled: (factor: number) => number;\n  isEven: () => boolean;\n};\n\ntype CounterConfig = PluginConfig<\n  'counter',\n  CounterOptions,\n  {},\n  {},\n  CounterSelectors\n>;\n\nexport const CounterPlugin = createTPlatePlugin<CounterConfig>({\n  key: 'counter',\n  options: {\n    value: 1,\n  },\n}).extendSelectors<CounterSelectors>(({ getOptions }) => ({\n  doubled: (factor) => getOptions().value * factor,\n  isEven: () => getOptions().value % 2 === 0,\n}));\n\nexport function CounterValue() {\n  const doubled = usePluginOption(CounterPlugin, 'doubled', 2);\n  const isEven = usePluginOption(CounterPlugin, 'isEven');\n  const value = usePluginOption(CounterPlugin, 'value');\n\n  return (\n    <span>\n      {value} / {doubled} / {isEven ? 'even' : 'odd'}\n    </span>\n  );\n}\n```\n\nSelectors 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.\n\n## API And Transforms\n\nUse API methods for reads and utilities. Use transforms for operations that mutate editor state.\n\n| Method | Access path | Typical use |\n| --- | --- | --- |\n| `.extendApi()` | `editor.api.counter.isEmpty()` | Plugin-specific query or utility. |\n| `.extendEditorApi()` | `editor.api.counterLabel()` | Editor-wide query or utility. |\n| `.extendTransforms()` | `editor.tf.counter.increment()` | Plugin-specific mutation. |\n| `.extendEditorTransforms()` | `editor.tf.resetCounter()` | Editor-wide mutation. |\n\n```tsx title=\"counter-plugin.tsx\" showLineNumbers\nimport { type PluginConfig } from 'platejs';\nimport { createTPlatePlugin } from 'platejs/react';\n\ntype CounterOptions = {\n  value: number;\n};\n\ntype CounterPluginApi = {\n  isEmpty: () => boolean;\n};\n\ntype CounterEditorApi = {\n  counterLabel: () => string;\n};\n\ntype CounterPluginTransforms = {\n  increment: () => void;\n};\n\ntype CounterEditorTransforms = {\n  resetCounter: () => void;\n};\n\ntype CounterConfig = PluginConfig<'counter', CounterOptions>;\n\nexport const CounterPlugin = createTPlatePlugin<CounterConfig>({\n  key: 'counter',\n  options: {\n    value: 0,\n  },\n})\n  .extendApi<CounterPluginApi>(({ getOption }) => ({\n    isEmpty: () => getOption('value') === 0,\n  }))\n  .extendEditorApi<CounterEditorApi>(({ getOption }) => ({\n    counterLabel: () => `Count: ${getOption('value')}`,\n  }))\n  .extendTransforms<CounterPluginTransforms>(({ getOption, setOption }) => ({\n    increment: () => setOption('value', getOption('value') + 1),\n  }))\n  .extendEditorTransforms<CounterEditorTransforms>(({ setOption }) => ({\n    resetCounter: () => setOption('value', 0),\n  }));\n```\n\nAfter the plugin resolves in an editor, call those methods from their resolved surfaces.\n\n```ts\neditor.api.counter.isEmpty();\neditor.api.counterLabel();\neditor.tf.counter.increment();\neditor.tf.resetCounter();\n```\n\n`editor.tf` is the short alias for `editor.transforms`; both access the same transform tree.\n\n## Override Editor Methods\n\nUse `.overrideEditor()` when you need to wrap existing editor API or transform methods and keep access to the original method.\n\n```tsx title=\"plugins.tsx\" showLineNumbers\nimport { createPlatePlugin } from 'platejs/react';\n\nexport const LimitExclamationPlugin = createPlatePlugin({\n  key: 'limitExclamation',\n}).overrideEditor(({ tf: { insertText } }) => ({\n  transforms: {\n    insertText(text, options) {\n      insertText(text === '!' ? '.' : text, options);\n    },\n  },\n}));\n```\n\nThe callback can override API methods, transform methods, or both.\n\n```tsx title=\"plugins.tsx\" showLineNumbers\nconst TrimStringPlugin = createPlatePlugin({\n  key: 'trimString',\n}).overrideEditor(({ api: { string } }) => ({\n  api: {\n    string(at, options) {\n      return string(at, options).trim();\n    },\n  },\n}));\n```\n\nKeep new editor methods in `.extendEditorApi()` or `.extendEditorTransforms()`. `.overrideEditor()` is for changing behavior while preserving the original call path.\n\n## Components\n\nUse `.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.\n\n```tsx title=\"plugins.tsx\" showLineNumbers\nimport { ParagraphPlugin } from 'platejs/react';\n\nimport { ParagraphElement } from '@/components/ui/paragraph-node';\n\nexport const AppParagraphPlugin =\n  ParagraphPlugin.withComponent(ParagraphElement);\n```\n\nUse `.withComponent()` for the common one-component case. Use `.extend()` when the same plugin also needs render wrappers, handlers, options, or rules.\n\n## Convert Slate Plugins\n\nUse `toPlatePlugin()` when you have a headless Slate plugin and need to add React-only fields such as `render`, `handlers`, or `useHooks`.\n\n```tsx title=\"mention-plugin.tsx\" showLineNumbers\nimport { createTSlatePlugin } from 'platejs';\nimport { toPlatePlugin } from 'platejs/react';\n\nimport { MentionElement } from '@/components/ui/mention-node';\n\nconst BaseMentionPlugin = createTSlatePlugin({\n  key: 'mention',\n  node: {\n    isElement: true,\n    isInline: true,\n  },\n});\n\nexport const MentionPlugin = toPlatePlugin(BaseMentionPlugin, {\n  render: {\n    node: MentionElement,\n  },\n});\n```\n\n`toPlatePlugin()` wraps the same plugin methods, so a converted plugin can still use `.configure()`, `.extend()`, `.extendApi()`, `.overrideEditor()`, and `.withComponent()`.\n\n## API Reference\n\n| Method | Accepts | Resolution behavior |\n| --- | --- | --- |\n| `.configure(config)` | Object or callback returning a partial plugin config. | Stores one configuration callback and applies it before function extensions. |\n| `.configurePlugin(plugin, config)` | Target plugin and object or callback config. | Recursively configures an existing nested plugin; missing target is ignored. |\n| `.extend(config)` | Object or callback returning a partial plugin config. | Object configs merge immediately; callback configs run during plugin resolution. |\n| `.extendPlugin(plugin, config)` | Target plugin and object or callback extension. | Recursively extends a nested plugin; missing target is added by key. |\n| `.extendSelectors(callback)` | Callback returning selector functions. | Extends the plugin option store selectors. |\n| `.extendApi(callback)` | Callback returning functions. | Merges into `editor.api[plugin.key]` and `plugin.api[plugin.key]`. |\n| `.extendEditorApi(callback)` | Callback returning functions or one-level nested function objects. | Merges into `editor.api` and `plugin.api`. |\n| `.extendTransforms(callback)` | Callback returning functions. | Merges into `editor.tf[plugin.key]` and `plugin.transforms[plugin.key]`. |\n| `.extendEditorTransforms(callback)` | Callback returning functions or one-level nested function objects. | Merges into `editor.tf` and `plugin.transforms`. |\n| `.overrideEditor(callback)` | Callback returning `{ api?, transforms? }`. | Deep-merges overrides into editor and plugin API/transform objects. |\n| `.withComponent(component)` | A node component. | Sets `node.component` and `render.node`. |\n| `.clone()` | No arguments. | Returns a merged copy of the plugin definition. |\n\nDone. Use configuration for existing fields, extension methods for new typed surface, and overrides only when the editor method already exists.\n",
      "type": "registry:file",
      "target": "content/docs/plate/(guides)/plugin-methods.mdx"
    }
  ],
  "type": "registry:file"
}