Browse by section

Web Design 日本語

Insert Images as img Tags in the Quill Editor

This article covers inserting images into the Quill editor as <img> tags.

The short answer: Quill’s built-in image button embeds the file as Base64 directly in the content, which is not viable in production. You need to replace it with an upload that inserts a URL instead.

Sponsored

Initialising the editor

const quill = new Quill('#editor', {
  theme: 'snow'
});

Why the default image button is a problem

Quill itself has no upload capability. The toolbar’s image button converts the selected file to Base64 and drops it straight into the document.

The stored HTML then contains the entire image as a string. A handful of photos turns a database row into hundreds of kilobytes of text, which slows every read of that record.

The fix is to send the file to the server and insert only the returned URL. You can do that with a third-party module, or by replacing the image handler yourself.

Sponsored

Using an upload module

Modules that add this typically take an endpoint and a pair of callbacks. Point uploadUrl at a route that stores the file and returns its URL.

const options = {
  uploadUrl: '/upload',
  method: 'POST',
  name: 'image',
  withCredentials: false,
  headers: {
    'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content
  },
  callbackOK: (serverResponse, next) => {
    next(serverResponse.url);
  },
  callbackKO: serverError => {
    console.log(serverError);
  }
};

Writing the handler yourself

You can replace just the image button’s behaviour without adding a module at all.

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

toolbar.addHandler('image', function () {
  const input = document.createElement('input');
  input.setAttribute('type', 'file');
  input.setAttribute('accept', 'image/*');
  input.click();

  input.onchange = async () => {
    const file = input.files[0];
    const formData = new FormData();
    formData.append('image', file);

    const res = await fetch('/upload', {
      method: 'POST',
      headers: { 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content },
      body: formData,
    });
    const data = await res.json();

    // insert at the current cursor position
    const range = quill.getSelection(true);
    quill.insertEmbed(range.index, 'image', data.url);
    quill.setSelection(range.index + 1);
  };
});

Read the CSRF token from a <meta> tag. Embedding Blade’s {{ csrf_token() }} directly into a JavaScript string stops working the moment the script moves to an external file.

<meta name="csrf-token" content="{{ csrf_token() }}">

getSelection(true) focuses the editor if focus has moved to the toolbar, which it will have. Without the argument there is no selection to read.

Related reading

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

The server side of the upload — validation, storage and the response — is covered in uploading Quill images to the server with Ajax.

To add buttons of your own to the toolbar, see adding a custom toolbar button to Quill.