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
grapesjsas apeerDependency, not a dependency. - Build an ESM + UMD bundle (Rollup, or the official GrapesJS plugin starter).
- Run
npm publish.
Tips for robust plugins
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.