# Can Plane Be Extended with Custom Plugins? A Guide to TipTap Extensions

> Yes Plane supports custom plugins using TipTap extensions. Learn how to pass custom extensions via the extensions prop in the @plane/editor package to enhance your Plane editor.

- Repository: [Plane/plane](https://github.com/makeplane/plane)
- Tags: how-to-guide
- Published: 2026-06-20

---

**Yes, Plane supports custom plugins through its TipTap-based editor architecture, allowing developers to pass custom extensions via the `extensions` prop in the `@plane/editor` package.**

Plane’s web editor is built on **TipTap**, a ProseMirror-based rich-text editor framework that provides a robust extension API. As an open-source project, Plane (makeplane/plane) exposes this extensibility directly to developers, enabling deep customization of the editing experience without modifying core internals. You can **extend Plane with custom plugins** by providing standard TipTap extensions to the editor component.

## How Plane’s Editor Architecture Supports Plugins

Plane’s editor component lives in the `packages/editor` package and is designed as a pure React component that accepts an `extensions` prop. According to the documentation in [`packages/editor/Readme.md`](https://github.com/makeplane/plane/blob/main/packages/editor/Readme.md), this prop accepts an array of TipTap extensions that are merged with the editor's built-in configuration.

Internally, the editor aggregates extensions in [`packages/editor/src/core/extensions/index.ts`](https://github.com/makeplane/plane/blob/main/packages/editor/src/core/extensions/index.ts), where each extension registers as a TipTap plugin using `new Plugin({ … })`. This architecture means any valid TipTap extension—whether official, community-built, or custom-authored—can be injected into Plane's editor instance.

## Creating Custom Extensions for Plane

Custom extensions in Plane are standard JavaScript or TypeScript objects that conform to TipTap’s `Extension` interface. You can implement three primary extension types to modify behavior:

### Adding Custom Mark Extensions

To introduce new formatting options like text highlighting, create a **Mark extension** that defines how the mark is parsed, rendered, and toggled:

```typescript
// my-extensions/highlight.ts
import { Extension } from '@tiptap/core';
import { Plugin, PluginKey } from 'prosemirror-state';
import { Mark } from '@tiptap/extension-mark';

export const HighlightExtension = Mark.create({
  name: 'highlight',
  parseHTML() {
    return [{ tag: 'span[data-highlight]' }];
  },
  renderHTML({ HTMLAttributes }) {
    return ['span', { ...HTMLAttributes, 'data-highlight': true }, 0];
  },
  addCommands() {
    return {
      toggleHighlight:
        () =>
        ({ commands }) => {
          return commands.toggleMark(this.name);
        },
    };
  },
});

```

Then pass the extension to the Editor component when rendering:

```tsx
// app.tsx – where the Plane editor is used
import { Editor } from '@plane/editor';
import { HighlightExtension } from './my-extensions/highlight';

function MyPage() {
  return (
    <Editor
      // Existing config is spread automatically; we add our extension
      extensions={[HighlightExtension]}
      // …other required props (e.g., value, onChange)…
    />
  );
}

```

### Hooking into the Transaction Pipeline

For advanced use cases like intercepting paste events or enforcing business rules, create an **Extension** that registers ProseMirror plugins:

```typescript
// my-extensions/file-upload.ts
import { Extension } from '@tiptap/core';
import { Plugin, PluginKey } from 'prosemirror-state';

export const FileUploadExtension = Extension.create({
  name: 'fileUpload',
  addProseMirrorPlugins() {
    return [
      new Plugin({
        key: new PluginKey('fileUpload'),
        props: {
          handlePaste(view, event) {
            // Custom logic to intercept pasted files and insert them
            // …
            return false; // let other handlers run if we didn't handle it
          },
        },
      }),
    ];
  },
});

```

Integrate this the same way by including it in the `extensions` array:

```tsx
// app.tsx
import { Editor } from '@plane/editor';
import { FileUploadExtension } from './my-extensions/file-upload';

function MyPage() {
  return (
    <Editor
      extensions={[FileUploadExtension]}
      // …
    />
  );
}

```

### Extending the Editor UI

The editor's menu system in [`packages/editor/src/core/components/menus/menu-items.ts`](https://github.com/makeplane/plane/blob/main/packages/editor/src/core/components/menus/menu-items.ts) provides patterns for adding **toolbar buttons** and context-menu items. When creating extensions that require UI controls, leverage this existing menu infrastructure to maintain consistency with Plane's design system.

## Key Source Files for Plugin Development

Understanding these specific files in the Plane repository helps when building custom plugins:

- **[`packages/editor/Readme.md`](https://github.com/makeplane/plane/blob/main/packages/editor/Readme.md)**: Documents the public API including the `extensions` prop that enables custom plugin support.
- **[`packages/editor/src/core/extensions/index.ts`](https://github.com/makeplane/plane/blob/main/packages/editor/src/core/extensions/index.ts)**: Shows how built-in extensions are registered and where your custom extensions merge into the configuration.
- **`packages/editor/src/core/plugins/*.ts`**: Contains reference implementations like [`markdown-clipboard.ts`](https://github.com/makeplane/plane/blob/main/markdown-clipboard.ts) and [`drag-handle.ts`](https://github.com/makeplane/plane/blob/main/drag-handle.ts) that demonstrate ProseMirror plugin patterns.
- **[`packages/editor/src/core/components/menus/menu-items.ts`](https://github.com/makeplane/plane/blob/main/packages/editor/src/core/components/menus/menu-items.ts)**: Defines toolbar and menu UI components for extending the editor interface.
- **[`packages/editor/src/index.ts`](https://github.com/makeplane/plane/blob/main/packages/editor/src/index.ts)**: The main export where the `Editor` React component receives your `extensions` array.

## Summary

- Plane's editor is built on TipTap and ProseMirror, making it inherently extensible through standard extension objects.
- Pass custom plugins via the `extensions` prop documented in [`packages/editor/Readme.md`](https://github.com/makeplane/plane/blob/main/packages/editor/Readme.md).
- Extensions can add new node types, intercept transactions via ProseMirror plugins, and integrate with Plane's menu system.
- No core modification is required; plugins live in your codebase and import into the Editor component.
- Reference implementations exist in `packages/editor/src/core/plugins/` for guidance on building advanced plugins.

## Frequently Asked Questions

### What plugin system does Plane use?

Plane uses **TipTap** extensions, which are built on top of ProseMirror plugins. This allows you to use any standard TipTap extension or create custom ones using the `@tiptap/core` API. The editor accepts these via the `extensions` prop as documented in [`packages/editor/Readme.md`](https://github.com/makeplane/plane/blob/main/packages/editor/Readme.md).

### Can I add custom toolbar buttons to Plane's editor?

Yes. You can extend the UI by creating extensions that interact with Plane's menu system defined in [`packages/editor/src/core/components/menus/menu-items.ts`](https://github.com/makeplane/plane/blob/main/packages/editor/src/core/components/menus/menu-items.ts). Your custom extension can register commands that are then bound to toolbar buttons or context menu items within the editor interface.

### Is it possible to intercept paste events in Plane?

Yes. By creating a custom extension that returns a ProseMirror plugin via `addProseMirrorPlugins()`, you can hook into the `handlePaste` event in the plugin's props. This allows you to intercept pasted content, handle file uploads, or transform clipboard data before it enters the document.

### Do custom plugins require rebuilding Plane from source?

No. Because the Plane editor exports a pure React component from [`packages/editor/src/index.ts`](https://github.com/makeplane/plane/blob/main/packages/editor/src/index.ts), you can import it into any external codebase, define your custom extensions locally, and pass them through the `extensions` prop without modifying the Plane repository itself.