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.

Class constructor

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

Public methods

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 Method returns HTML that will be appended at the top of Block-settings
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

Static getters

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.

Lifecycle hooks

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.

render()

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

Parametres

This method has no arguments.

Return value

Element Tool's UI wrapper

Example

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.

save()

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.

Parametres

Element Element created by render method.

Return value

Promise.<Object> or Object Block data to save.

Example

class MyTool { // ... other methods /** * Return Block Data * @param {Element} blockContent - element returned by Render method * @return {object} */ save(blockContent) { return { text: blockContent.innerHTML; } } }

validate()

Optional method.

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

Parametres

Object Data returned by save method.

Return value

Boolean Validation result. Return false to skip Block or true if data is correct.

Example

class MyTool { // ... other methods /** * Validates data * @param {object} savedData - object returned by 'save' method */ validate(savedData) { if (savedData.text.trim() === "") { return false; } return true; } }

renderSettings()

Method returns UI of the Block settings. 

Parametres

This method has no arguments

Return value

Element Single element that contains a Block Settings UI elements.

Example

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; } }
☝️
Tip.
You can use Styles API to get common Editor.js classes for Block Actions elements: settingButton and settingButtonActive

destroy

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.

Parametres

This method has no arguments

Return value

This method should not return anything.

Example

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

onPaste

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.

Parametres

CustomEvent Custom paste event with detail property.

Return value

This method should not return anything.

Example

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 }

merge

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.

Parametres

Object Block data in same format that returned by save method

Return value

This method should not return anything.

Example

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; } }

pasteConfig

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

Return value

{tags: string[], files: { mimeTypes: string[], extensions: string[] }, patterns: { [string]: RegEx }} Paste configuration.

Example

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.

sanitize

Object that defines rules for automatic Sanitizing

Return value

Object Sanitizer configuration rules for each of saved field.

Example

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.

toolbox

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

Return value

{icon: string, title: string} Icon and Title for the Toolbox

Example

class SimpleImage { // ... static get toolbox() { return { icon: '<i class="my-tool-icon"></i>', title: 'Simple-Image' } } }
☝️
Tip.
The icon property can contain SVG code of icon.

shortcut

Shortcut to apply Tool's render and inserting behaviour

Return value

String Shortcut for Tool inserting. Format described here.

Example

class SimpleImage { // ... static get shortcut() { return 'CMD+SHIFT+I'; } }

conversionConfig

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.

Example

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 }; } }

Your Tool -> other Tool

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 }; } }

Other Tool -> your Tool

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' }; } }; } }

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.

Return value

Boolean If true, Editor won't handle Enter keydowns for this tool 

Example

/** * 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 }

isReadOnlySupported

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

Return value

Boolean If true, editor.js can be rendered with the read-only mode with your tool

Example

/** * 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.