Plugin Methods

PreviousNext

Explore the various methods available for extending Plate plugins.

Docs
platefile

Preview

Loading preview…
../../docs/(guides)/plugin-methods.mdx
---
title: Plugin Methods
description: Explore the various methods available for extending Plate plugins.
---

## Configuration Methods

When extending plugins, all properties are deeply merged by default, with two exceptions: arrays are replaced entirely, and the `options` object is shallow merged.

### .configure

The `.configure` method allows you to override the plugin's configuration.

```ts
const ConfiguredPlugin = MyPlugin.configure({
  options: {
    myOption: 'new value',
  },
});
```

You can also use a function to access the current configuration:

```ts
const ConfiguredPlugin = MyPlugin.configure(({ getOptions }) => ({
  options: {
    ...getOptions(),
    myOption: `${getOptions().myOption} + extra`,
  },
}));
```

- It's used to modify existing properties of the plugin.
- It doesn't add new properties to the plugin.
- The last configuration applied is the one used by the editor.
- It doesn't return an extended type, maintaining the original plugin type.

### .configurePlugin

The `.configurePlugin` method allows you to configure the properties of a nested plugin:

```ts
const TablePlugin = createPlatePlugin({
  key: 'table',
  plugins: [TableCellPlugin],
}).configurePlugin(TableCellPlugin, {
  options: {
    cellOption: 'modified',
  },
});
```

- It's used to configure nested plugins within a parent plugin.
- Like `.configure`, it modifies existing properties but doesn't add new ones.
- It's useful for adjusting the behavior of sub-plugins without extending their types.


### .extend

The `.extend` method allows you to extend the plugin's configuration and functionality.

```ts
const ExtendedPlugin = MyPlugin.extend({
  options: {
    newOption: 'new value',
  },
});
```

You can also use a function to access the current configuration and editor:

```ts
const ExtendedPlugin = MyPlugin.extend(({ editor, plugin }) => ({
  options: {
    newOption: 'new value',
  },
  handlers: {
    onKeyDown: () => {
      // Custom key down logic
    },
  },
}));
```

- It's used to add new properties or modify existing ones in the plugin.
- It returns a new plugin instance with extended types.
- It's chainable, allowing multiple extensions to be applied sequentially.

### .extendPlugin

The `.extendPlugin` method allows you to extend the configuration and functionality of a nested plugin:

```ts
const TablePlugin = createPlatePlugin({
  key: 'table',
  plugins: [TableCellPlugin],
}).extendPlugin(TableCellPlugin, {
  options: {
    newCellOption: 'added',
  },
  handlers: {
    onKeyDown: () => {
      // Custom key down logic for table cells
    },
  },
});
```

- It's used to extend nested plugins within a parent plugin.
- It can add new properties and modify existing ones in the nested plugin.
- It returns a new parent plugin instance with the extended nested plugin.


### Difference between .configure and .extend

While both methods can be used to modify plugin configuration, they have some key differences:

1. Chaining: `.extend` is chainable, while `.configure` is not.
2. Type extension: `.extend` returns a new plugin instance with extended types, while `.configure` maintains the original type.
3. New properties: `.extend` can add new properties to the plugin configuration, while `.configure` only modifies existing ones.

Choose the appropriate method based on whether you need to extend the plugin's type and functionality (use `.extend`) or simply modify existing configuration (use `.configure`).

### .extendSelectors

The `extendSelectors` method allows you to add subscribable selectors to your plugin:

```ts
const CounterPlugin = createPlatePlugin({
  key: 'counter',
  options: {
    count: 0,
  },
}).extendSelectors(({ getOptions }) => ({
  doubleCount: () => getOptions().count * 2,
  isEven: () => getOptions().count % 2 === 0,
}));
```

You can then use those selectors in your components or other plugin methods:

```tsx
const CounterComponent = () => {
  const count = usePluginOption(CounterPlugin, 'count');
  const doubleCount = usePluginOption(CounterPlugin, 'doubleCount');
  const isEven = usePluginOption(CounterPlugin, 'isEven');

  return (
    <div>
      <p>Count: {count}</p>
      <p>Double Count: {doubleCount}</p>
      <p>Is Even: {isEven ? 'Yes' : 'No'}</p>
    </div>
  );
};
```

- It allows you to create derived state or computed values from plugin options.
- Read the value using `getOption`
- Subscribe to the value using `usePluginOption`, recomputed whenever options change, re-rendering only when the result changes. **This is the main difference with `.extendApi`**.

### .withComponent

The `withComponent` method allows you to set or replace the component associated with a plugin.

```ts
const ParagraphPlugin = createPlatePlugin({
  key: 'p',
  node: {
    isElement: true,
    type: 'p',
  },
}).withComponent(ParagraphElement);
```

## API and Transforms

Plugins can define their own API methods and transforms that will be merged into the editor's API and transforms. This is done using the `extendApi`, `extendEditorApi`, `extendTransforms`, and `extendEditorTransforms` methods.

### .extendApi

Use `extendApi` to add plugin-specific API methods:

```ts
const MyPlugin = createPlatePlugin({
  key: 'myPlugin',
}).extendApi(() => ({
  pluginMethod: () => 'plugin method result',
}));

// Access the plugin's API
editor.api.myPlugin.api.pluginMethod();
```

### .extendEditorApi

Use `extendEditorApi` to add root-level API methods:

```ts
const MyPlugin = createPlatePlugin({
  key: 'myPlugin',
}).extendEditorApi(({ getOptions }) => ({
  editorMethod: () => getOptions().someOption,
}));

// Access the plugin's API
editor.api.editorMethod();
```

### .extendTransforms

Use `extendTransforms` to add plugin-specific transform methods:

```ts
const MyPlugin = createPlatePlugin({
  key: 'myPlugin',
}).extendTransforms(() => ({
  pluginTransform: () => {
    // Custom transform logic
  },
}));

// Access the plugin's transform
editor.tf.myPlugin.pluginTransform();

// NOTE: `editor.tf` in a short alias to `editor.transforms`
editor.transforms.myPlugin.pluginTransform();
```

### .extendEditorTransforms

Use `extendEditorTransforms` to add root-level transform methods:

```ts
const MyPlugin = createPlatePlugin({
  key: 'myPlugin',
}).extendEditorTransforms(({ editor }) => ({
  editorTransform: () => {
    // Custom editor transform logic
  },
}));

// Access the plugin's transform
editor.tf.editorTransform();
```

### .overrideEditor

The `overrideEditor` method is used specifically for overriding existing editor methods without altering the plugin's type:

```ts
const MyPlugin = createPlatePlugin({
  key: 'myPlugin',
}).overrideEditor(({ editor, tf: { insertText }, api: { isInline } }) => ({
  transforms: {
    insertText(text, options) {
      // Override insertText behavior
      insertText(text, options);
    },
  },
  api: {
    isInline(element) {
      // Override isInline behavior
      return isInline(element);
    },
  },
}));
```

- Used specifically for overriding existing editor methods
- Returns the overridden methods wrapped in `transforms` or `api` objects
- Cannot add new methods (use `extendEditorTransforms` or `extendEditorApi` instead)
- Provides access to original methods through the context

### Difference between API and Transforms

While there is currently no core difference between API and Transforms in Plate, they serve distinct purposes and are designed with future extensibility in mind:

- **Transforms:** 
  - Store all Slate transforms and editor operations here. Structured to potentially support middlewares in the future, allowing for more complex transform pipelines and devtools.
  - Typically used for operations that modify the editor state, such as inserting, deleting, or transforming content.
  - Example: `editor.tf.toggleBlock()`, `editor.tf.toggleMark('bold')`

- **API:** 
  - Store all queries, utility functions, and other methods that don't directly modify the editor state.
  - Example: `editor.api.save()`, `editor.api.debug.log()`

### Chaining Extensions

You can chain these methods to create a comprehensive plugin:

```ts
const MyPlugin = createPlatePlugin({
  key: 'myPlugin',
  options: {
    baseValue: 5,
  },
})
  .extendApi(() => ({
    pluginMethod: () => 'plugin method',
  }))
  .extendEditorApi(({ getOptions }) => ({
    multiply: (factor: number) => getOptions().baseValue * factor,
  }))
  .extendTransforms(() => ({
    pluginTransform: () => {
      // Plugin-specific transform
    },
  }))
  .extendEditorTransforms(({ editor }) => ({
    editorTransform: () => {
      // Editor-specific transform
    },
  }));

editor.api.myPlugin.api.pluginMethod();
editor.api.multiply(3);
editor.tf.myPlugin.pluginTransform();
editor.tf.editorTransform();
```

## Convert a Slate Plugin to a Plate Plugin

To convert a typed Slate plugin to a Plate plugin, you can use `toPlatePlugin`:

```ts
const CodeBlockPlugin = toPlatePlugin(createSlatePlugin({ key: 'code_block' }), {
  handlers: {},
  options: { hotkey: ['mod+opt+8', 'mod+shift+8'] },
});
```

Installation

npx shadcn@latest add @plate/plugin-methods-docs

Usage

Usage varies by registry entry. Refer to the registry docs or source files below for details.