DEV Community

Cover image for Adding Quill Editor to a Livewire-Alpine.js App
Eduardo Guzmán
Eduardo Guzmán

Posted on

Adding Quill Editor to a Livewire-Alpine.js App

In case you are not familiar with the stack, this is a little tech reference:

  • Livewire - Livewire is a full-stack framework for Laravel that takes the pain out of building dynamic UIs.
  • Alpine.js - Alpine.js is a rugged, minimal framework for composing JavaScript behavior in your markup.
  • Quill - Quill is a modern WYSIWYG editor built for compatibility and extensibility.

Using third party libraries in Livewire is a very common use case. This time I faced the need to add a WYSIWYG editor and wanted to show how it can be achieved.

The very short response is shown in this snippet. Next, I will break down every piece.

This is how your Livewire component will look like.

<!-- 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>
Enter fullscreen mode Exit fullscreen mode

Now, let's break down the pieces that are working here.

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.

<!-- 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
Enter fullscreen mode Exit fullscreen mode

Next, add a <div> element in which we will be mounting Quill. Alpine.js will be used to mount the Editor into this element.

x-data will tell Alpine.js to declare a new component scope in this element, this way we can use other Alpine.js features.


x-ref=quillEditor will let us reference this DOM element in a very convenient way. We will be able to use $refs.quillEditor later when we need to reference this element.


x-init= will tell Apline.js to run an expression when this component is initialized. That is why we added the x-data on that component.

At this point we can already use Quill, we will initialize it and add its event handler.

First, we create a new instance of Quill and use the $ref.quillEditor to bind the editor into this Element.

Then, we use the same instance and add an event handler to it. The event handler will listen to the text-change event. When this event happens, we will fetch the HTML that has been added to the editor and by using Alpine.js's $dispatch feature, we will emit an input event in this component.

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

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

wire:model.debounce.3000ms="description". Given that we are now reproducing an input event, we can use Livewires data binding feature. Addingwire:model="description"` will bind the value of the Quill editor right into our Livewire component. This way we can have this data in our Laravel app.

Adding debounce.2000ms to the data binding will debounce these many input events and update the value of the property after 2000 ms.


wire:ignore Adding this to a parent element will tell Livewire to skip DOM-diffing for this element, this is useful because Quill adds elements that Livewire does not know from the original HTML. This will prevent a small page flicker when re-rendering Livewire components.

Closing

That's it, very easy and right into your HTML Markup.

The same approach will work with many other third party libraries.

Enjoy the Livewire - AlpineJs Stack.

Top comments (10)

Collapse
 
james0r profile image
James Auble

Thanks for this! If someone is already just using vanilla javascript with an input element changing value of it on('text-change'), they can use inputElement.dispatchEvent(new Event('input')) after changing the value to fix the problem with wire:model.

Collapse
 
ayoubbousetta profile image
AYOUB BOUSETTA

Undefined index: value

Collapse
 
eduarguz profile image
Eduardo Guzmán
Collapse
 
xattab4 profile image
Vladislav

This is why it doesn't work correctly github.com/livewire/livewire/issue...
The problem is in the editor.

Collapse
 
xattab4 profile image
Vladislav

In the playground, the same error. I can't figure out why.

Thread Thread
 
eduarguz profile image
Eduardo Guzmán

I think this is working now...

laravelplayground.com/#/snippets/0...

Let me know if this works so I can update the post

Thread Thread
 
xattab4 profile image
Vladislav

It definitely works. Could you tell me more about how to upload images using quill to the server. In this blog post?

Collapse
 
sarfraznawaz2005 profile image
Sarfraz Ahmed

In laravel, the issue of validation can be solved like: 'content' => [
'required',
function ($attribute, $value, $fail) {
if (!trim(strip_tags($value))) {
$fail('The content field is required.');
}
},
],

Collapse
 
r1chardwheatley profile image
Richard Wheatley

Super elegant solution. Livewire 3 I switched set for $wire and worked a treat.

Collapse
 
jeroenvanrensen profile image
Jeroen van Rensen

Hi, I followed your tutorial and it was great! But is it also possible to auto-detect images and upload them using Livewire?