Table of contents

Tools API

This article describes all available options for Tools creation. See «Creating a Block Tool» or «Creating an Inline Tool» guide series for usage examples and more detailed explanations.

Each Tool can be implemented by a class. It's constructor accepts three useful arguments:

class MyTool { constructor({data, api, config, readOnly, block}){ // ... use or store arguments as you want } }
data Previously saved data. Can be used to update UI state on article editing.
api Editor.js Core API object
config User configuration object that passed through the initial Editor configuration.
readOnly The flag determines whether the read-only mode enabled or not. You should disable data modifications ability and change the UI as well in this mode.
block BlockAPI object with methods to work with Block instance
render Required Creates UI of a Block
save Required Extracts Block data from the UI
validate Optional Validates Block data after saving. If returns false, Block will be skipped on Editor saving.
renderSettings Optional Returns configuration for the tunes that will be displayed in Block Tunes menu
destroy Optional Contain logic for clear Tools stuff: cache, variables, events. Called when Editor instance is destroying.
onPaste Optional Handle content pasted by ways that described by pasteConfig static getter.
merge Optional Method specifies how to merge two similar Blocks
pasteConfig Allows your Tool to substitute pasted HTML tags, files or URLs.
sanitize Automatic sanitize configuration. Allows to clean unwanted HTML tags or attributes from files with Inline Toolbar.
toolbox Required if Tools should be added to the Toolbox. Describe an icon and title here.
shortcut Shortcut that fires render method and inserts new Block
conversionConfig Config allows Tool to specify how it can be converted into/from another Tool.
enableLineBreaks With this option, Editor.js won't handle Enter keydowns. Can be helpful for Tools like Code where line breaks should be handled by default behavior.
isReadOnlySupported This flag tells core that current tool supports the read-only mode.

Define these public methods to handle any state of Block's lifecycle

rendered Called after Block contents is added to the page
updated Called each time Block contents is updated
removed Called after Block contents is removed from the page but before Block instance deleted
moved(event: MoveEvent) Called after Block is moved by move tunes (or through API). MoveEvent extends CustomEvent interface, its details field contains the object with fromIndex and toIndex properties.

Create a Tool UI here. Should return single HTML Element that contains all your interface.

This method has no arguments.

Element Tool's UI wrapper
class MyTool { render(){ const wrapper = document.createElement('div'); wrapper.contentEditable = true; return wrapper; } }

Don't forget to fill the UI with previously saved data passed to class constructor.

Only for Block Tools.

Inversely to the render method, it should extract actual data from the UI and return a Promise returned data of the Block.

Element Element created by render method.
Promise.<Object> or Object Block data to save.
class MyTool { // ... other methods /** * Return Block Data * @param {Element} blockContent - element returned by Render method * @return {object} */ save(blockContent) { return { text: blockContent.innerHTML; } } }

Optional method.

Validates saved data to skip invalid (for example, empty) Blocks.

Object Data returned by save method.
Boolean Validation result. Return false to skip Block or true if data is correct.
class MyTool { // ... other methods /** * Validates data * @param {object} savedData - object returned by 'save' method */ validate(savedData) { if (savedData.text.trim() === "") { return false; } return true; } }

Method allows to define block tunes. Can either return Tunes Menu configuration or single HTML element with block tunes UI elements.

This method has no arguments

TunesMenuConfig Configuration of tunes appearance inside Block Tunes menu
Element Single element that contains a Block Settings UI elements.
☝️
Note
Custom HTML return value format is only recommended for corner cases when it's not possible to define tune with TunesMenuConfig. In other cases use simple config format.
class SimpleImage { // ... other methods renderSettings(){ return [ { icon: `<svg width="20" height="20" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M15.8 10.592v2.043h2.35v2.138H15.8v2.232h-2.25v-2.232h-2.4v-2.138h2.4v-2.28h2.25v.237h1.15-1.15zM1.9 8.455v-3.42c0-1.154.985-2.09 2.2-2.09h4.2v2.137H4.15v3.373H1.9zm0 2.137h2.25v3.325H8.3v2.138H4.1c-1.215 0-2.2-.936-2.2-2.09v-3.373zm15.05-2.137H14.7V5.082h-4.15V2.945h4.2c1.215 0 2.2.936 2.2 2.09v3.42z"/></svg>`, label: 'With border' }, { icon: `<svg width="17" height="10" viewBox="0 0 17 10" xmlns="http://www.w3.org/2000/svg"><path d="M13.568 5.925H4.056l1.703 1.703a1.125 1.125 0 0 1-1.59 1.591L.962 6.014A1.069 1.069 0 0 1 .588 4.26L4.38.469a1.069 1.069 0 0 1 1.512 1.511L4.084 3.787h9.606l-1.85-1.85a1.069 1.069 0 1 1 1.512-1.51l3.792 3.791a1.069 1.069 0 0 1-.475 1.788L13.514 9.16a1.125 1.125 0 0 1-1.59-1.591l1.644-1.644z"/></svg>`, label: 'Stretched' }, { icon: `<svg width="20" height="20" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M10.043 8.265l3.183-3.183h-2.924L4.75 10.636v2.923l4.15-4.15v2.351l-2.158 2.159H8.9v2.137H4.7c-1.215 0-2.2-.936-2.2-2.09v-8.93c0-1.154.985-2.09 2.2-2.09h10.663l.033-.033.034.034c1.178.04 2.12.96 2.12 2.089v3.23H15.3V5.359l-2.906 2.906h-2.35zM7.951 5.082H4.75v3.201l3.201-3.2zm5.099 7.078v3.04h4.15v-3.04h-4.15zm-1.1-2.137h6.35c.635 0 1.15.489 1.15 1.092v5.13c0 .603-.515 1.092-1.15 1.092h-6.35c-.635 0-1.15-.489-1.15-1.092v-5.13c0-.603.515-1.092 1.15-1.092z"/></svg>`, label: 'With background' } ]; } }
class SimpleImage { // ... other methods renderSettings(){ const settings = [ { name: 'withBorder', icon: `<svg width="20" height="20" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M15.8 10.592v2.043h2.35v2.138H15.8v2.232h-2.25v-2.232h-2.4v-2.138h2.4v-2.28h2.25v.237h1.15-1.15zM1.9 8.455v-3.42c0-1.154.985-2.09 2.2-2.09h4.2v2.137H4.15v3.373H1.9zm0 2.137h2.25v3.325H8.3v2.138H4.1c-1.215 0-2.2-.936-2.2-2.09v-3.373zm15.05-2.137H14.7V5.082h-4.15V2.945h4.2c1.215 0 2.2.936 2.2 2.09v3.42z"/></svg>` }, { name: 'stretched', icon: `<svg width="17" height="10" viewBox="0 0 17 10" xmlns="http://www.w3.org/2000/svg"><path d="M13.568 5.925H4.056l1.703 1.703a1.125 1.125 0 0 1-1.59 1.591L.962 6.014A1.069 1.069 0 0 1 .588 4.26L4.38.469a1.069 1.069 0 0 1 1.512 1.511L4.084 3.787h9.606l-1.85-1.85a1.069 1.069 0 1 1 1.512-1.51l3.792 3.791a1.069 1.069 0 0 1-.475 1.788L13.514 9.16a1.125 1.125 0 0 1-1.59-1.591l1.644-1.644z"/></svg>` }, { name: 'withBackground', icon: `<svg width="20" height="20" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M10.043 8.265l3.183-3.183h-2.924L4.75 10.636v2.923l4.15-4.15v2.351l-2.158 2.159H8.9v2.137H4.7c-1.215 0-2.2-.936-2.2-2.09v-8.93c0-1.154.985-2.09 2.2-2.09h10.663l.033-.033.034.034c1.178.04 2.12.96 2.12 2.089v3.23H15.3V5.359l-2.906 2.906h-2.35zM7.951 5.082H4.75v3.201l3.201-3.2zm5.099 7.078v3.04h4.15v-3.04h-4.15zm-1.1-2.137h6.35c.635 0 1.15.489 1.15 1.092v5.13c0 .603-.515 1.092-1.15 1.092h-6.35c-.635 0-1.15-.489-1.15-1.092v-5.13c0-.603.515-1.092 1.15-1.092z"/></svg>` } ]; const wrapper = document.createElement('div'); settings.forEach( tune => { let button = document.createElement('div'); button.classList.add('cdx-settings-button'); button.innerHTML = tune.icon; wrapper.appendChild(button); }); return wrapper; } }

Method will be fired when Editor's instance is destroying with destroy API method. Clear your Tool's stuff here: remove event listeners, clear nodes cache and null variables and properties.

This method has no arguments

This method should not return anything.

class SimpleImage { // ... destroy() { this.input.removeEventListener('keydown', this.keydown); this.input = null; } }

After you set a pasteConfig, for each of paste substitutions (tags, files, and patterns) you need just one method — onPaste. It accepts CustomEvent object as argument which can have three types: tag, file, and pattern. Each event provides info about pasted content in detail property.

  1. Tag event contains pasted HTML element in detail.data property.
  2. File event contains pasted file object in detail.file property.
  3. Pattern event contains pasted string in detail.data property and pattern name in detail.key.
CustomEvent Custom paste event with detail property.

This method should not return anything.

class ImageTool { // ... other Tool methods static get pasteConfig() { return { tags: ['IMG'], files: { mimeTypes: ['image/*'], extensions: ['gif', 'jpg', 'png'] // Or you can specify extensions }, patterns: { image: /https?:\/\/\S+\.(gif|jpe?g|tiff|png)$/i } } } onPaste(event) { switch(event.type) { case 'tag': const element = event.detail.data; // this is <img> element this.data = { url: element.src }; break; case 'pattern': const url = event.detail.data; const key = event.detail.key; // equals 'image' this.data = { url: url } break; case 'file': /* We need to read file here as base64 string */ const file = event.detail.file; const reader = new FileReader(); reader.onload = (loadEvent) => { this.data = { url: loadEvent.target.result; }; }; reader.readAsDataURL(file); break; } } // ... other Tool methods }

Method that specifies how to merge two similar Blocks, for example on Backspace keypress. It accepts data object in same format as the Render and it should provide behaviour how to combine new data with the currently stored value.

Object Block data in same format that returned by save method

This method should not return anything.

class Header { // ... other methods /** * @param {{text: string, level: number}} data - Header Tool Data */ merge(data) { this.data.text += data.text; this.wrapper.innerHTML += data.text; } }

Static getter pasteConfig shoud return object with paste substitutions configuration:

  1. tags — array of tags to substitute
  2. files — object with mimeTypes and extensions arrays
  3. patterns — object where key is your pattern name and RegExp pattern as value
{tags: string[], files: { mimeTypes: string[], extensions: string[] }, patterns: { [string]: RegEx }} Paste configuration.

For example for Image Tool we need to substitute img tags, handle files with image/* MIME-type and handle URLs to images:

class ImageTool { // ... other Tool methods static get pasteConfig() { return { tags: ['IMG'], files: { mimeTypes: ['image/*'], extensions: ['gif', 'jpg', 'png'] // Or you can specify extensions }, patterns: { image: /https?:\/\/\S+\.(gif|jpe?g|tiff|png)$/i } } } // ... other Tool methods }

Then you need to specify onPaste handler to implement logic how to make your Tool from pasted content. See more information about paste substitution here.

Object that defines rules for automatic Sanitizing

Object Sanitizer configuration rules for each of saved field.
class SimpleImage { // ... static get sanitize() { return { url: false, // disallow HTML caption: { b: true, // leave <b> a: { href: true }, // leave <a> with attribute href } } } }
☝️
Read more about available sanitizer rules here.

Defines icon and title of the Tool's button inside the Toolbox. This getter is required for Block Tools that should be rendered in the Toolbox. 

{icon: string, title: string} Icon and Title for the Toolbox
{icon: string, title: string, data: any}[] In multiple entries case returns array of each entry configuration. Note the data field — it should contain data overrides that will be passed to the Block constructor. The data object should fit the structure of the Block’s output data or be a part of it.
class SimpleImage { // ... static get toolbox() { return { icon: '<i class="my-tool-icon"></i>', title: 'Simple-Image' } } }

To render several variants for one Tool in the Toolbox, toolbox getter should return an array:

class Tool { // ... static get toolbox() { return [ { icon: '<svg> ... </svg>', // icon for H1, title: 'Heading 1', data: { level: 1, }, }, { icon: '<svg> ... </svg>', // icon for H2, title: 'Heading 2', data: { level: 2, }, }, { icon: '<svg> ... </svg>', // icon for H3, title: 'Heading 3', data: { level: 3, }, }, ]; } // ... }

Such toolbox configuration will make the Tool appear in the Toolbox multiple times which will look as follows

☝️
Tip.
The icon property can contain SVG code of icon.

Shortcut to apply Tool's render and inserting behaviour

String Shortcut for Tool inserting. Format described here.
class SimpleImage { // ... static get shortcut() { return 'CMD+SHIFT+I'; } }

Editor.js has a Conversion Toolbar that allows user to convert one Block to another.

  1. You can add ability to your Tool to be converted. Specify «export» property of conversionConfig
  2. You can add ability to convert other Tools to your Tool. Specify «import» property of conversionConfig
☝️
Note.
Conversion Toolbar will be shown only near Blocks that specified an «export» rule, when user selected almost entire block's content. This Toolbar will contain only Tools that specified an «import» rule.
class Header { constructor(){ this.data = { text: '', level: 2 } } /** * Rules specified how our Tool can be converted to/from other Tool. */ static get conversionConfig() { return { export: 'text', // this property of tool data will be used as string to pass to other tool import: 'text' // to this property imported string will be passed }; } }

The «export» field specifies how to represent your Tool's data as a string to pass it to other tool.

It can be a String or a Function

String means a key of your Tool data object that should be used as string to export.

Function is a method that accepts your Tool data and compose a string to export from it. See example below:

class ListTool { constructor(){ this.data = { items: [ 'Fisrt item', 'Second item', 'Third item' ], type: 'ordered' } } static get conversionConfig() { return { export: (data) => { return data.items.join('.'); // in this example, all list items will be concatenated to an export string }, // ... import rule }; } }

he «import» rule specifies how to create your Tool's data object from the string created by original block.

It can be a String or a Function

String means the key in tool data that will be filled by an exported string.

For example, import: 'text' means that constructor of your block will accept a data object with text property filled with string composed by original block.

Function allows you to specify own logic, how a string should be converted to your tool data. For example:

class ListTool { constructor(data){ this.data = data || { items: [], type: 'unordered' } } static get conversionConfig() { return { // ... export rule /** * In this example, List Tool creates items by splitting original text by a dot symbol. */ import: (string) => { const items = string.split('.'); return { items: items.filter( (text) => text.trim() !== ''), type: 'unordered' }; } }; } }

With this option, Editor.js won't handle Enter keydowns. Can be helpful for Tools like Code where line breaks should be handled by default behavior.

Boolean If true, Editor won't handle Enter keydowns for this tool 
/** * Code Tool for the Editor.js allows to include code examples in your articles. */ class CodeTool { /** * Allow pressing Enter inside the CodeTool textarea * * @returns {boolean} * @public */ static get enableLineBreaks() { return true; } // ... tool's code }

Use this flag to tell the editor that your tool can be rendered in the read-only mode

Boolean If true, editor.js can be rendered with the read-only mode with your tool
/** * Code Tool for the Editor.js allows to include code examples in your articles. */ class CodeTool { /** * Notify core that the read-only mode is supported * * @returns {boolean} * @public */ static get isReadOnlySupported() { return true; } // ... tool's code }
☝️
Tip
Your tool will accept the readOnly option along with other constructor parameters. You need to disable edition and support read-only UI by yourself.