Plugins

plugins#

Type: object | string[]

Default: {}

Enables plugin instances for the editor. Import (or include) each plugin so it registers with Redactor.addPlugin, then pass plugin keys (camelCase) in the plugins setting.

The usual form is a string array:

const app = Redactor('#entry', {
  plugins: ['counter']
});

Several plugins:

const app = Redactor('#entry', {
  plugins: ['counter', 'emoji', 'textStats']
});

You can also pass an object map (useful when you need per-plugin options in the same place — see Plugin options below):

const app = Redactor('#entry', {
  plugins: {
    textStats: {
      plugin: 'textStats',
      options: {
        containerClass: 'my-counter'
      }
    }
  }
});

The instance key (array item, or map key) is used for getPlugin, app[key], commands (textStats.open), and option merging.

Connect plugins#

Plugins live under plugins/{slug}/ in the package. Connect them after the Redactor core, then enable them with plugins as shown above.

Script tags (no bundler)

Include each plugin after redactor.js:

<link rel="stylesheet" href="redactor.css">
<script src="redactor.js"></script>
<script src="plugins/counter/counter.js"></script>
<script>
const app = Redactor('#entry', {
  plugins: ['counter']
});
</script>

ES modules / bundlers (Vite, React, Vue, …)

Import Redactor, then import the plugin module (side-effect). The plugin registers itself with Redactor.addPlugin:

import Redactor from './redactor.esm.min.js';
import './redactor.css';
import './plugins/counter.js';

const app = Redactor('#entry', {
  plugins: ['counter']
});

Plugin keys#

Plugin keys use camelCase. Multi-word names are not hyphenated. The folder and filename in the package use kebab-case (the slug):

Key Script path in the package
textStats plugins/text-stats/text-stats.js
blockClass plugins/block-class/block-class.js
definedLinks plugins/defined-links/defined-links.js
alignment plugins/alignment/alignment.js

Single-word plugins stay lowercase (anchor, math, counter). The key in plugins must match the name passed to Redactor.addPlugin.

When you use the object map with plugin: 'name', Redactor resolves the class in this order:

  1. Redactor.getPlugin(name) — plugins registered with Redactor.addPlugin
  2. window.RedactorPlugins[name] — fallback for legacy/global bundles

If the class cannot be resolved and debug: true, a warning is printed and the entry is skipped.

Custom plugins you author yourself can call Redactor.addPlugin(key, YourPluginClass) from your code, then enable them with the same string-key pattern. See Create a plugin.

Plugin options#

Options are merged in this order (later wins):

  1. PluginClass.defaultOptions — defaults defined on the plugin class
  2. plugins[key].options — per-instance options in the plugins map
  3. Top-level [key] — options under the same camelCase name as the instance key

Top-level options (typical)

Pass plugin options at the top level by plugin key, not inside plugins:

const app = Redactor('#entry', {
  plugins: ['textStats'],
  textStats: {
    containerClass: 'my-counter'
  }
});
const app = Redactor('#entry', {
  plugins: ['alignment', 'math'],
  alignment: {
    context: true
  },
  math: {
    classname: 'katex'
  }
});

Options inside the plugins map

const app = Redactor('#entry', {
  plugins: {
    textStats: {
      plugin: 'textStats',
      options: {
        containerClass: 'my-counter'
      }
    }
  }
});

Inside a plugin, merged options are available as this.options. Some plugins also read this.app.getOption('pluginKey') for the same merged object.