> ## Documentation Index
> Fetch the complete documentation index at: https://docs.yoopta.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Plugins

> Understanding and working with Yoopta Editor plugins

## Overview

Plugins are the building blocks of Yoopta Editor. Each plugin represents a specific type of content block (paragraph, heading, image, etc.) and defines how that content is rendered and behaves.

Every plugin in Yoopta Editor is built using the `YooptaPlugin` class, which provides a consistent API for defining block types, handling events, managing state, and extending functionality.

## Plugin Architecture

### YooptaPlugin Class

All plugins are created using the `YooptaPlugin` class constructor:

```typescript theme={null}
import { YooptaPlugin } from '@yoopta/editor';

const MyPlugin = new YooptaPlugin({
  type: 'MyPlugin',
  elements: {
    // Plugin elements definition
  },
  options: {
    // Plugin options
  },
  commands: {
    // Plugin commands
  },
  events: {
    // Event handlers
  },
  lifecycle: {
    // Lifecycle hooks
  },
  parsers: {
    // HTML, Markdown, Email parsers
  },
  extensions: {
    // Slate extensions
  },
});
```

### Plugin Structure

Each plugin configuration consists of:

<ResponseField name="type" type="string" required>
  Unique identifier for the plugin (e.g., 'Paragraph', 'Image', 'Code')
</ResponseField>

<ResponseField name="elements" type="object | JSX.Element" required>
  Defines the structure and rendering of plugin elements. Can be an object or JSX syntax
</ResponseField>

<ResponseField name="options" type="object">
  Configuration options including display settings and shortcuts
</ResponseField>

<ResponseField name="commands" type="object">
  Methods for manipulating plugin content programmatically
</ResponseField>

<ResponseField name="events" type="object">
  Event handlers for keyboard, mouse, and other DOM events
</ResponseField>

<ResponseField name="lifecycle" type="object">
  Hooks that run at specific points in the plugin lifecycle
</ResponseField>

<ResponseField name="parsers" type="object">
  Serialization and deserialization for HTML, Markdown, and Email
</ResponseField>

<ResponseField name="extensions" type="function">
  Slate editor extensions for custom behavior
</ResponseField>

## Available Plugins

Yoopta Editor provides 20+ built-in plugins:

| Category       | Plugins                                                                                                                                                                                           |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Text**       | [Paragraph](/plugins/paragraph), [Headings](/plugins/headings) (H1, H2, H3), [Blockquote](/plugins/blockquote)                                                                                    |
| **Lists**      | [BulletedList, NumberedList, TodoList](/plugins/lists)                                                                                                                                            |
| **Media**      | [Image](/plugins/image), [Video](/plugins/video), [File](/plugins/file), [Embed](/plugins/embed)                                                                                                  |
| **Layout**     | [Table](/plugins/table), [Accordion](/plugins/accordion), [Tabs](/plugins/tabs), [Steps](/plugins/steps), [Carousel](/plugins/carousel), [Callout](/plugins/callout), [Divider](/plugins/divider) |
| **Code**       | [Code](/plugins/code), [CodeGroup](/plugins/code-group)                                                                                                                                           |
| **Inline**     | [Link](/plugins/link), [Mention](/plugins/mention), [Emoji](/plugins/emoji)                                                                                                                       |
| **Navigation** | [TableOfContents](/plugins/table-of-contents)                                                                                                                                                     |

## Using Plugins

Plugins are passed to **`createYooptaEditor`**, not to `<YooptaEditor>`. The component only receives the `editor` instance.

### Basic Usage

```jsx theme={null}
import { useMemo } from 'react';
import YooptaEditor, { createYooptaEditor } from '@yoopta/editor';
import Paragraph from '@yoopta/paragraph';
import Blockquote from '@yoopta/blockquote';
import Code from '@yoopta/code';

const plugins = [Paragraph, Blockquote, Code];

export default function Editor() {
  const editor = useMemo(
    () => createYooptaEditor({ plugins, marks: [] }),
    [],
  );

  return (
    <YooptaEditor
      editor={editor}
      onChange={(value) => {}}
      placeholder="Type / to open menu"
    />
  );
}
```

### Headings Plugin

The Headings plugin exports multiple heading types:

```jsx theme={null}
import Headings from '@yoopta/headings';

const plugins = [
  Headings.HeadingOne, // H1
  Headings.HeadingTwo, // H2
  Headings.HeadingThree, // H3
];
```

### Lists Plugin

The Lists plugin exports multiple list types:

```jsx theme={null}
import Lists from '@yoopta/lists';

const plugins = [Lists.BulletedList, Lists.NumberedList, Lists.TodoList];
```

## Plugin Configuration

Most plugins accept configuration options when you call `plugin.extend({ options: { ... } })`. Pass the resulting plugins array to `createYooptaEditor({ plugins, marks })`, not to `<YooptaEditor>`.

### Image Plugin

```jsx theme={null}
import Image from '@yoopta/image';

const plugins = [
  Image.extend({
    options: {
      async onUpload(file) {
        // Custom upload logic
        const url = await uploadToServer(file);
        return { src: url };
      },
      maxWidth: 1000,
      minWidth: 100,
    },
  }),
];
```

### Code Plugin

```jsx theme={null}
import Code from '@yoopta/code';

const plugins = [
  Code.extend({
    options: {
      theme: 'github-dark',
      language: 'javascript',
      languages: ['javascript', 'typescript', 'python', 'rust'],
    },
  }),
];
```

### Video Plugin

```jsx theme={null}
import Video from '@yoopta/video';

const plugins = [
  Video.extend({
    options: {
      async onUpload(file) {
        const url = await uploadVideo(file);
        return { src: url, provider: 'custom' };
      },
      maxSize: 100 * 1024 * 1024, // 100MB
      accept: 'video/*',
    },
  }),
];
```

## Plugin Methods

### extend()

Extend a plugin with custom options or elements (UI):

```jsx theme={null}
const CustomParagraph = Paragraph.extend({
  options: {
    display: { title: 'Text', description: 'Plain text' },
    shortcuts: ['p'],
  },
  elements: {
    paragraph: {
      render: (props) => (
        <p className="my-paragraph" {...props.attributes}>
          {props.children}
        </p>
      ),
    },
  },
});
```

### Defining Elements

Elements can be defined in two ways:

**1. Object Syntax:**

```typescript theme={null}
const MyPlugin = new YooptaPlugin({
  type: 'MyPlugin',
  elements: {
    'my-element': {
      render: (props) => <div {...props.attributes}>{props.children}</div>,
      props: {
        /* default props */
      },
      nodeType: 'block', // or 'void' or 'inline'
    },
  },
});
```

**2. JSX Syntax (Recommended):**

```typescript theme={null}
const MyPlugin = new YooptaPlugin({
  type: 'MyPlugin',
  elements: (
    <my-container render={MyContainer}>
      <my-item render={MyItem} />
    </my-container>
  ),
});
```

### Element Properties

<ParamField path="render" type="function" required>
  React component that renders the element
</ParamField>

<ParamField path="props" type="object">
  Default props for the element
</ParamField>

<ParamField path="nodeType" type="'block' | 'void' | 'inline'">
  Type of Slate node. Default is 'block'
</ParamField>

<ParamField path="placeholder" type="string">
  Placeholder text shown when this element is focused and empty. Set via `.extend()`:

  ```jsx theme={null}
  HeadingOne.extend({
    elements: {
      'heading-one': { placeholder: 'Heading 1' },
    },
  })
  ```

  Element-level placeholders take priority over the editor-level `placeholder` prop. For nested plugins (Steps, Accordion), each element can have its own placeholder. See [Editor Placeholders](/core/editor#placeholders) for the full guide.
</ParamField>

## Custom elements (renders)

Override default rendering by extending with custom `elements`:

```jsx theme={null}
import Paragraph from '@yoopta/paragraph';

const CustomParagraph = Paragraph.extend({
  elements: {
    paragraph: {
      render: (props) => (
        <p className="custom-paragraph" {...props.attributes}>
          {props.children}
        </p>
      ),
    },
  },
});
```

## Plugin Events

Handle plugin-specific events:

```jsx theme={null}
const CustomImage = Image.extend({
  events: {
    onPaste: (editor, slate, options) => {
      return (event) => {
        console.log('onPaste', event);
      };
    },
    onKeyDown: (editor, slate, options) => {
      return (event) => {
        console.log('onKeyDown', event);
      };
    },
  },
});
```

## Plugin shortcuts

Configure slash-menu shortcuts via `options.shortcuts` (array of strings). Plugin-specific hotkeys are defined in the plugin's `events.onKeyDown` or in options. Use `Blocks.toggleBlock(editor, 'Code', { preserveContent: true })` for programmatic block type change.

## Common Plugin Options

Most plugins support these common options:

<ParamField path="display" type="object">
  Display configuration - `title` - Display name in UI - `description` - Plugin description - `icon`

  * Custom icon component
</ParamField>

<ParamField path="shortcuts" type="string[]">
  Keyboard shortcuts to trigger the plugin
</ParamField>

<ParamField path="slate" type="object">
  Slate-specific configuration
</ParamField>

## Plugin Loading Order

The order of plugins in the array affects:

1. Menu display order
2. Transform priority
3. Shortcut precedence

```jsx theme={null}
const plugins = [
  Paragraph, // Shows first in menus
  Headings.HeadingOne,
  Headings.HeadingTwo,
  // ...
  Divider, // Shows last in menus
];
```

## Best Practices

* Always include the **Paragraph** plugin — it's the default block type
* Define your `plugins` array **outside the component** to prevent recreation on every render
* Use **`extend()`** to customize plugins instead of modifying plugin objects directly
* For plugins with async operations (uploads), always handle errors and loading states

## Lifecycle Hooks

Lifecycle hooks allow you to run code at specific points in a block's lifecycle:

```typescript theme={null}
const MyPlugin = new YooptaPlugin({
  type: 'MyPlugin',
  lifecycle: {
    beforeCreate: (editor) => {
      // Return initial block structure
      return editor.y('my-element', {
        children: [editor.y.text('Initial content')],
      });
    },
    onCreate: (editor, block) => {
      // Run after block is created
    },
    onDestroy: (editor, block) => {
      // Cleanup before block is destroyed
    },
  },
});
```

### Available Lifecycle Hooks

<ResponseField name="beforeCreate" type="(editor: YooEditor) => SlateElement">
  Called before a block is created. Return the initial structure
</ResponseField>

<ResponseField name="onCreate" type="(editor: YooEditor, block: YooptaBlockData) => void">
  Called after a block is created
</ResponseField>

<ResponseField name="onDestroy" type="(editor: YooEditor, block: YooptaBlockData) => void">
  Called before a block is destroyed
</ResponseField>

## Parsers

Parsers handle serialization and deserialization of content:

```typescript theme={null}
const MyPlugin = new YooptaPlugin({
  type: 'MyPlugin',
  parsers: {
    html: {
      deserialize: {
        nodeNames: ['DIV'],
        parse: (el) => {
          // Convert HTML element to Slate node
          return {
            type: 'my-element',
            children: [{ text: el.textContent }],
          };
        },
      },
      serialize: (element, text, blockMeta) => {
        // Convert Slate node to HTML string
        return `<div>${text}</div>`;
      },
    },
    markdown: {
      serialize: (element, text) => {
        // Convert to Markdown
        return `${text}\n`;
      },
    },
    email: {
      serialize: (element, text) => {
        // Convert to email-compatible HTML
        return `<table><tr><td>${text}</td></tr></table>`;
      },
    },
  },
});
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Paragraph Plugin" icon="paragraph" href="/plugins/paragraph">
    Learn about the basic text plugin
  </Card>

  <Card title="Image Plugin" icon="image" href="/plugins/image">
    Working with images and uploads
  </Card>

  <Card title="Code Plugin" icon="code" href="/plugins/code">
    Syntax-highlighted code blocks
  </Card>

  <Card title="Table Plugin" icon="table" href="/plugins/table">
    Create and manage tables
  </Card>
</CardGroup>
