Browse by section

Web Design 日本語

Add a Custom Toolbar Button to the Quill Editor

This article covers adding a custom button to the Quill editor’s toolbar.

The short answer: put a button with a ql- prefixed class in your toolbar markup, then register a handler under the same name in handlers. The appearance lives in HTML, the behaviour in JavaScript.

This article shipped with an incorrect implementation in 2022. It has been rewritten in September 2026 with code that works on Quill 2.0.3.

Sponsored

Adding a custom button

Write the toolbar as HTML and hand the container to Quill. Name your button’s class ql-something and you can register a handler for something.

The toolbar markup:

<div id="toolbar">
  <button class="ql-bold"></button>
  <button class="ql-italic"></button>
  <!-- the custom one -->
  <button class="ql-insertStar">★</button>
</div>

<div id="editor"></div>

Names Quill already knows, such as ql-bold, get their icon filled in automatically if you leave the button empty. Quill does not know your custom name, so supply the content yourself.

Then pass the container in and register the handler:

const quill = new Quill('#editor', {
  theme: 'snow',
  modules: {
    toolbar: {
      container: '#toolbar',
      handlers: {
        // the key is the class name minus the ql- prefix
        insertStar: function () {
          const range = this.quill.getSelection(true);
          this.quill.insertText(range.index, '★');
          this.quill.setSelection(range.index + 1);
        }
      }
    }
  }
});

The handler key is the class name with ql- removed. ql-insertStar maps to insertStar. If they do not match, clicking the button does nothing at all.

Inside a handler, this is the toolbar module, so reach the editor through this.quill.

Registering a handler after initialisation

For an editor that is already running, use addHandler().

const toolbar = quill.getModule('toolbar');

toolbar.addHandler('insertStar', function () {
  const range = this.quill.getSelection(true);
  this.quill.insertText(range.index, '★');
});

The same method overrides a built-in button. A common use is replacing the image button with your own upload flow:

toolbar.addHandler('image', function () {
  openImageUploader();
});

Styling the button

Target it by the class you gave it.

.ql-toolbar .ql-insertStar {
  font-size: 18px;
  color: #444;
}

.ql-toolbar .ql-insertStar:hover {
  color: #06c;
}

Quill’s own buttons contain SVGs. To recolour those, target the stroke and the fill:

.ql-toolbar button:hover .ql-stroke {
  stroke: #06c;
}

.ql-toolbar button:hover .ql-fill {
  fill: #06c;
}

.ql-stroke is the outline and .ql-fill is the solid area. Different buttons use different ones, so if a colour will not change, try the other.

Sponsored

Using an image or icon font

The button’s contents are ordinary HTML, so an <svg> or an icon font both work.

<button class="ql-insertStar">
  <i class="fa-solid fa-star"></i>
</button>

With Font Awesome, use the version 7 class names. The version 4 style, fa fa-star, renders nothing in current releases.

Common problems

If the button does nothing, check the class name against the handler key first.

▼Likely causes

Symptom Cause
Nothing happens on click Class name and handler key do not match
Two toolbars appear Passing an array as well as a container
The button renders blank Quill does not know the name and you left it empty
The insertion point is wrong getSelection(true) was called without true

The true argument to getSelection() means “focus the editor if it is not focused”. Clicking a toolbar button moves focus away from the editor, so without it there is no selection to read and the call fails.

Summary

It comes down to one rule: the class name (ql-something) and the handler key (something) must match.

If Quill is not set up yet, start with adding the Quill rich text editor to Laravel. Quill 2.0 changed the CDN host, so older snippets will not load.

To extend the built-in dropdowns, see customising font-family and customising font-size.