How to Add Quill Editor to Livewire Alpinejs web App Project

In this tutorial how to add Quill Editor to livewire and alpine.js for our new web app projects we have used Livewire, Alpine and Quill editor for the stack. We always import third-party libraries into our Livewire projects and in this we are adding QUill WYSIWYG editor in it.
Here is a brief review of each before starting on adding Quill to our Alpine and Livewire project.
Quill editor is a cutting edge new WYSIWYG that helps dev to extend web app projects and make them compatible as per needs.
We use Laravel Livewire framework for our high end responsive and dynamic UIs because it provdes complete stack to the developers. Here is a piece of code that we will break down to show up the steps using Quill on Livewire-alpine.js

<!-- livewire/edit-component.blade.php -->
@push('styles')
<link href="https://cdn.quilljs.com/1.3.6/quill.snow.css" rel="stylesheet">
@endpush

@push('scripts')
<script src="https://cdn.quilljs.com/1.3.6/quill.js"></script>
@endpush

<div class="mt-2 bg-white" wire:ignore>
<div
x-data
x-ref="quillEditor"
x-init="
quill = new Quill($refs.quillEditor, {theme: 'snow'});
quill.on('text-change', function () {
$dispatch('input', quill.root.innerHTML);
});
"
wire:model.debounce.2000ms="description"
>
{!! $description !!}
</div>
</div>

Next we are discussing each part in detail.
In order to use Quill we need to import the library into our webpage. We use a CDN that will provide the css and the js that is required.
We are adding the <link> and <script> tags by using laravel blade stacks.
We will use blade.php and import library for Quill by using a CDN for CSS and JavaScript.

<!-- layouts/base.blade.php -->
<html>
<head>
@stack('styles')
</head>
<body>
@stack('scripts')
</body>
</html>

<!-- livewire/edit-component.blade.php -->
@push('styles')
<link href="https://cdn.quilljs.com/1.3.6/quill.snow.css" rel="stylesheet">
@endpush

@push('scripts')
<script src="https://cdn.quilljs.com/1.3.6/quill.js"></script>
@endpush

Now we need to add a <div> to mount Quill editor and use Alpine to to the editor in the <div> element. The x-data tells Alpine for declaring new component and we get to use more Alpine features this way in the new component scope.

We will reference this DOM using the x-ref=quillEditor with quite an ease and then use $refs.quillEditor while we have to reference it. We used x-data on this component so that x-init= is used to inform Alpine.js to run an expression as this component prompts.

You need to know that now we may use Quill at this stage. For instance we can initialize and add Quill event handler to it. Then we make use of the $ref.quillEditor to get the editor into this element but first we will create a new instance for Quill to do that.

Next we will add the event handler to listen the text-change event initiated in the same instance. Then retrieve by fetching the HTML which was included into the editor as that even happened. And also we use $dispatch feature of Alpine and emit to that component an input event.

<div
...
x-init="
quill = new Quill($refs.quillEditor, {theme: 'snow'});

quill.on('text-change', function () {
$dispatch('input', quill.root.innerHTML);
});
"
>...</div>

wire:model.debounce.3000ms=”description”. In this instance we have to use livewire’s binding feature wire:model=”description” while reproducing the input event. Now that feature binds the value of Quill editor into the Livewire componen and we have that data present in the Laravel app seemlessly.

So if we add debounce.2000ms into the bind-data the input events will debounce and property value will be updated after 2000ms.

QUill may add elements from the HTML that the Livewire does not know about so adding ‘wire:ignore’ to the parent element tells Livewire for skipping DOM-diffing for it. So as we will re-render the Livewire components it avoids the page flicker at the end of it. Then it is there into the HTML and it can work for tons of other libraries.

However we can also create a hidden input for storing value and also attach the wire:model in order to listen to the Quill instance ‘text-change event’. Next fire input event on input element for syncing model’s value.

function quillEditor(data) {
return {
instance: null,
init() {
this.$nextTick(() => {
this.instance = new Quill(this.$refs.editor, {
theme: 'snow'
});

this.instance.on('text-change', () => {
this.$refs.input.dispatchEvent(new CustomEvent('input', {
detail: this.instance.root.innerHTML
}));
})
})
},
...data
}
}
<div x-data="quillEditor({})" x-init="init()">
<input type="hidden" x-ref="input" wire:model="description">

<div wire:ignore>
<div x-ref="editor">{!! $description !!}</div>
</div>
</div>

If you don’t use ‘model.defer’ the following should work for you if you ran into an error somehow.

<div
x-data
x-ref="quillfilling"

wire:model.lazy="Product.filling" // defer gives the same error

x-init="
filling = new Quill($refs.quillfilling, { modules: {
// formula: true,
// syntax: true,
toolbar: '.toolbar-container-quillfilling'
},theme: 'snow'});

filling.on('text-change', function () {
justHtmltest_filling = filling.root.innerHTML;

@this.set('Product.filling', justHtmltest_filling)


});
"

>
{!! $Product['filling'] !!}
`</div>

While if you are using Vanilla JavaScript along with the input element to change its value on ‘text-change’ then using the inputElement.dispatchEvent(new Event(‘input’)) after the new value will fix the ‘wire:model’ issue in your laravel app project.