Add the Quill Rich Text Editor to Laravel (Quill 2.0)
This article covers adding the Quill rich text editor to a Laravel Blade view.
The short answer: load two files from a CDN and write one line of new Quill(). No server-side setup and no build step.
Published in 2021 and revised in September 2026. The Quill 1.3.6 used originally has been superseded by the 2.0 line, released in 2024, and both the CDN host and the URLs have changed. Every snippet below is updated to 2.0.3, and the last section covers what breaks when upgrading from 1.x.
Adding Quill to a Blade view
Load the CDN files in the head, then add a div for the editor and a line of script in the body.
Quill can be downloaded from GitHub, but a CDN is simpler here. Quill 2.0 is distributed through jsDelivr at cdn.jsdelivr.net/npm/quill@2.0.3/dist/. The cdn.quilljs.com URLs used by 1.x do not serve 2.0 files, so copying them from an older guide will not work.
<head>
<script src="https://cdn.jsdelivr.net/npm/quill@2.0.3/dist/quill.js"></script>
<link href="https://cdn.jsdelivr.net/npm/quill@2.0.3/dist/quill.snow.css" rel="stylesheet">
</head>
<body>
<div id="quill_editor"></div>
<script>
const quill = new Quill('#quill_editor', {
theme: 'snow'
});
</script>
</body>
That is the whole integration.
What each part does
The CDN in the head
Two files are required: the JavaScript, and a stylesheet for the theme you use. Quill ships two themes, and each has its own stylesheet.
<script src="https://cdn.jsdelivr.net/npm/quill@2.0.3/dist/quill.js"></script>
<link href="https://cdn.jsdelivr.net/npm/quill@2.0.3/dist/quill.snow.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/quill@2.0.3/dist/quill.bubble.css" rel="stylesheet">
The editor container in the body
Quill turns an existing element into an editor. Give it an id to target.
<div id="quill_editor"></div>
Creating the editor
Pass the selector and an options object. theme is the only one required to get started.
const quill = new Quill('#quill_editor', {
theme: 'snow'
});
Switching themes
Quill has two themes: snow shows a fixed toolbar above the editor, bubble shows one only when text is selected.
snow
The toolbar sits above the editing area at all times. It is the more conventional choice and works well on desktop.
bubble
The toolbar appears next to the selection and is hidden otherwise. It saves vertical space, which suits narrow screens.
Customising the toolbar
Pass a toolbar array under modules to choose which buttons appear and how they are grouped.
Each inner array becomes a visual group in the toolbar.
const toolbarOptions = [
[{ 'header': [1, 2, 3, 4, false] }],
[{ 'align': [] }],
['bold', 'italic', 'underline'],
[{ 'color': [] }, { 'background': [] }],
[{ 'list': 'ordered' }, { 'list': 'bullet' }],
[{ 'indent': '-1' }, { 'indent': '+1' }],
['image'],
['video'],
['link']
];
const quill = new Quill('#quill_editor', {
modules: {
toolbar: toolbarOptions
},
placeholder: 'Write your content here',
theme: 'snow'
});
Leaving out buttons is as important as adding them. A toolbar offering every option encourages inconsistent formatting; restricting it to what your templates actually render keeps stored content predictable.
Saving the content to Laravel
Quill does not submit its content with a form on its own. Add a hidden input and fill it on submit.
Quill edits the contents of a <div>, which is not a form control and is therefore never posted. Move the value across just before submission:
<form method="POST" action="/posts" id="post_form">
@csrf
<div id="quill_editor"></div>
<input type="hidden" name="body" id="body">
<button type="submit">Save</button>
</form>
<script>
const quill = new Quill('#quill_editor', { theme: 'snow' });
document.getElementById('post_form').addEventListener('submit', function () {
document.getElementById('body').value = quill.getSemanticHTML();
});
</script>
getSemanticHTML() is new in Quill 2.0. It returns HTML with Quill’s internal classes stripped out, which is what you want for storage. The 1.x habit of reading quill.root.innerHTML includes those internal classes and is a poor fit for content you intend to render elsewhere.
When rendering it back out, Blade escapes by default. Use {!! !!} to output HTML — but rendering user input unescaped is an XSS risk, so sanitise it either before storing or before display.
{!! $post->body !!}
To load existing content into the editor, use dangerouslyPasteHTML() after initialisation:
quill.clipboard.dangerouslyPasteHTML(@json($post->body));
Upgrading from Quill 1.x to 2.0
Beyond the CDN URLs, the structure of the stored HTML changes. If you already have content saved from 1.x, your display CSS may break.
▼Key changes
| Item | Quill 1.x | Quill 2.0 |
|---|---|---|
| CDN | cdn.quilljs.com/1.3.6/ |
cdn.jsdelivr.net/npm/quill@2.0.3/dist/ |
| Lists | <ul> and <ol> |
All <ol>, with the type in an attribute |
| Code blocks | A dedicated element | <div> |
| Reading HTML | root.innerHTML |
getSemanticHTML() added |
| Internet Explorer | Supported | Dropped |
| Type definitions | @types/quill needed |
Bundled; remove the package |
Lists are the change most likely to bite. In 2.0 unordered lists are emitted as <ol> with the type carried in an attribute. If your display CSS targets ul, bullets saved after the upgrade will look different from ones saved before it.
Check that your rendering CSS does not assume ul before upgrading a site with existing content, because old and new markup will coexist.
The strict and scrollingContainer options were removed, and the clipboard’s pasteHTML is gone. Code carried over unchanged from 1.x tends to fail on those three.
Going further
To widen the font and size choices in the toolbar, see customising font-family in Quill and customising font-size in Quill. Both use the same registration pattern.
If you are still setting up the Laravel side, building a Laravel and Blade environment with Docker covers the environment this editor sits in.