How to create a custom GrapesJS plugin (step by step, 2026)

Build a custom GrapesJS plugin from scratch: the plugin function signature, registering blocks, commands, and components, and shipping it as an npm package.

DevFuture Development
DevFuture Development
Apr 15, 20262 months ago
6 min read1 views

What a GrapesJS plugin actually is

A GrapesJS plugin is just a function that receives the editor and an options object: (editor, opts) => { /* register things */ }. Inside it you add blocks, commands, components, panels, or storage. There is no special class to extend. This guide builds a small but real plugin and packages it for npm.

1. The plugin function

// src/index.js
export default (editor, opts = {}) => {
  const options = {
    label: 'My Block',
    category: 'My Plugin',
    ...opts,                       // caller overrides win
  };

  editor.BlockManager.add('my-block', {
    label: options.label,
    category: options.category,
    content: '<section class="my-block"><h2>Hello</h2></section>',
  });
};

2. Use it in an editor

import grapesjs from 'grapesjs';
import myPlugin from './src/index.js';

grapesjs.init({
  container: '#gjs',
  plugins: [myPlugin],
  pluginsOpts: {
    // keyed by plugin name when loaded by string; for a function, pass opts here
  },
});

When you load a plugin by its package name string, GrapesJS reads its options from pluginsOpts['package-name'].

3. Add a command and a button

editor.Commands.add('say-hi', {
  run: () => editor.Modal.open({ title: 'Hi', content: 'From my plugin' }),
});

editor.Panels.addButton('options', {
  id: 'say-hi',
  className: 'fa fa-smile-o',
  command: 'say-hi',
});

4. Package it for npm

  • Export the plugin function as the package default.
  • List grapesjs as a peerDependency, not a dependency.
  • Build an ESM + UMD bundle (Rollup, or the official GrapesJS plugin starter).
  • Run npm publish.

Tips for robust plugins

Developer working on a laptop at a desk
Namespace ids, merge defaults, and clean up on destroy.

A few habits separate a throwaway script from a shippable plugin. Namespace every id you register (blocks, commands, components) with a prefix so two plugins never collide. Always merge a defaults object with the incoming opts and read from the result, so callers can override behaviour without editing your code. Declare grapesjs as a peerDependency, not a dependency, so the host app controls the version. Clean up anything you attach (DOM listeners, timers) on the editor's destroy event. And document your options — a plugin nobody can configure gets forked, not reused.

Prerequisites

You need a working GrapesJS setup and basic JavaScript. A plugin is just a function (editor, opts) => {} — there is no class to extend and no build step to start. You only need a bundler (Vite, Webpack, or Rollup) when you package the plugin for distribution.

A fuller plugin: block + command + button

Real plugins usually register several things at once. Here a single plugin adds a block, a command, and a toolbar button:

export default (editor, opts = {}) => {
  const options = { label: 'Hi', ...opts };

  editor.BlockManager.add('callout', {
    label: 'Callout',
    category: 'Custom',
    content: '<div class="callout">Note</div>',
  });

  editor.Commands.add('say-hi', {
    run: () => editor.Modal.open({ title: options.label, content: 'From my plugin' }),
  });

  editor.Panels.addButton('options', {
    id: 'say-hi', className: 'fa fa-smile-o', command: 'say-hi',
  });
};

Clean up on destroy

If your plugin attaches DOM listeners, timers, or external resources, release them on the editor's destroy event so embedding the editor repeatedly (common in SPA routing) doesn't leak:

const onScroll = () => {/* … */};
window.addEventListener('scroll', onScroll);
editor.on('destroy', () => window.removeEventListener('scroll', onScroll));

Best practices

Namespace every id you register (blocks, commands, components) with a prefix so two plugins never collide. Always merge a defaults object with the incoming opts. Declare grapesjs as a peerDependency so the host app controls the version. Keep one responsibility per plugin and document its options — a plugin nobody can configure gets forked, not reused.

Package it for npm

Export the function as the package default, build an ESM + UMD bundle (Rollup or the official GrapesJS plugin starter), list grapesjs as a peer dependency, and run npm publish. Consumers load it by name and pass options via pluginsOpts.

Next steps

Want a head start? Generate a plugin scaffold with the GJS.Market plugin generator, then dive into adding custom blocks and custom components. Browse the full GrapesJS plugins catalog or start from the GJS.Market home page.

FAQ

What is a GrapesJS plugin?

A function (editor, opts) => {} that registers blocks, commands, components, panels, or storage. You pass it to grapesjs.init via the plugins array.

How do I add options to a plugin?

Merge a defaults object with the incoming opts and read from the result. Callers set them via pluginsOpts.

How do I publish to npm?

Export the function as the default, declare grapesjs as a peer dependency, build a bundle, and run npm publish.

Published Apr 15, 2026
Updated Jun 27, 2026
🔌 GJS.Market

Looking for GrapesJS plugins?

Over 100 curated plugins, presets, and templates — hand-picked for quality and maintained by the community.

Share this postTwitterFacebookLinkedIn
Published via
DevFuture Development
DevFuture Development
Visit shop →

More from DevFuture Development

Discover other insightful posts and stay updated with the latest content.

View all posts

Premium plugins from DevFuture Development

Hand-picked paid additions crafted by this creator.

Visit shop →