Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Livewire Quill Laravel Package

joelwmale/livewire-quill

A Laravel Livewire component for the Quill WYSIWYG editor. Add rich text fields to Livewire forms, bind content with wire:model, customize the toolbar, and get clean HTML output without writing JavaScript.

View on GitHub
Deep Wiki
Context7

Getting Started

This package wraps the Quill rich-text editor as a Livewire component, so the fastest path is: install it, confirm which component alias it registers, then bind it to a model property you already round-trip to the database.

  1. composer require joelwmale/livewire-quill
  2. Publish the config and assets (the tagged publish groups cover both, typically livewire-quill:config and livewire-quill:assets). If the asset tag doesn't exist in your version, check the package's README and src/LivewireQuillServiceProvider.php for the exact tag names before assuming something is broken.
  3. Make sure Quill's own CSS/JS is loaded — the package ships the editor integration, but if you're on your own layout you'll need the Quill stylesheet in <head> (or the package's Blade directives if your version ships them). A completely unstyled editor usually means missing Quill CSS, not a broken component.
  4. Drop the component into a form and bind it:
<form wire:submit="save">
    <livewire:quill-editor wire:model="content" />
    @error('content') <span>{{ $message }}</span> @enderror
    <button type="submit">Save</button>
</form>

Confirm the actual tag with php artisan livewire:list (or livewire:list equivalent in Livewire 4) rather than copying a tag from a blog post — the component name has changed across major versions, and a wrong alias gives you a confusing "component not found" rather than a helpful error.

v5.4.0 specifically: this release is additive — livewire/livewire is widened to ^3.0|^4.0 and Livewire 4 is now covered in CI. There are no breaking changes and no migration steps. If you were pinned to Livewire 3 solely because of this package, you can now upgrade Livewire first and the editor will keep working. Note the CI matrix: PHP 8.2–8.4 is tested against both Livewire 3 and 4, but PHP 8.5 is only tested against Livewire 4 — if you're on 8.5, treat Livewire 4 as required, not optional.

First real use case: a CMS/Article $content field on an edit form, with server-side sanitisation on save (see Gotchas).

Implementation Patterns

Treat the editor as an uncontrolled HTML field, not a Livewire-owned input. Quill mutates its own DOM; Livewire's morphing wants to own it. The reliable pattern is to wrap in wire:ignore and let the component bridge changes into Livewire:

<div wire:ignore>
    <livewire:quill-editor wire:model="content" />
</div>

Without this, any parent re-render can wipe the editor contents or reset the cursor mid-typing.

Choose your binding mode deliberately. wire:model (deferred) is correct for most forms — content syncs on the next Livewire request. wire:model.live will fire a round trip on every keystroke/change event, which is expensive for a large HTML payload. If you need live validation, prefer .live.debounce.500ms or .blur. Reserve truly-live binding for word-count/preview panels where the latency is user-visible anyway.

Put it in a dedicated form component, not inline in a Blade page. A ArticleForm Livewire component with public string $content = '', rules() and save() keeps the editor's state lifecycle contained and makes the editor testable independently of page layout. Pass initial content via mount():

public function mount(?Article $article = null): void
{
    $this->content = $article?->content ?? '';
}

Key the component when you swap records. If you reuse the same form component across multiple articles (e.g. a slide-over), add wire:key="editor-{{ $article->id }}" on the wrapper. Without it, Quill keeps the previous document's content because it never re-initialises.

Image uploads are a configuration exercise, not code. The package's config file is where you set the upload disk, path and validation (size/mime). Publish the config, point it at your public disk, and run php artisan storage:link. If images upload but render as broken links, it's almost always the disk/APP_URL mismatch rather than the editor.

Sanitise on save — always. Quill produces HTML and the package will happily hand you <script>-adjacent markup from a paste. Run content through an allowlist sanitizer (e.g. HTMLPurifier via mews/purifier) in the component's save() or, better, in a form request / model mutator so every write path is covered:

$this->validate();
$article->update(['content' => clean($this->content)]);

Store HTML, not Delta. Persist the HTML representation in a text/longText column so the same content renders on the front end without needing Quill on the read path. Storing Delta JSON locks you into re-hydrating through the editor.

Testing: use Livewire::test(ArticleForm::class)->set('content', '<p>Hello</p>')->call('save') and assert the DB value. The editor's JS bridge is not exercised in these tests, which is fine — you're testing your state handling, and that's where the bugs actually are. Add one browser/Dusk-level smoke test that types into the editor if the editor's wiring is business-critical.

Gotchas and Tips

  • wire:ignore is the number one support issue. If content "randomly resets" or "jumps to the top while typing", you're missing it, or you have it in the wrong place (it must wrap the editor, not sit inside it).
  • Double initialisation on re-render. When the wrapper re-renders without a wire:key, Quill instances can stack. Symptom: duplicate toolbars or duplicated typing. Fix with keys, not with JS hacks.
  • Config changes need a cache clear. php artisan config:clear after editing config/livewire-quill.php — a cached config in a container is a classic "my toolbar change did nothing".
  • Assets and @livewireStyles/@livewireScripts ordering. Quill must not load before Livewire's Alpine/Livewire runtime in a way that breaks the component's init. If the editor renders but is inert, check script order first, then the browser console.
  • Livewire 4 note. v5.4.0 widens the constraint and adds Livewire 4 to CI, but the two majors differ in asset injection and DOM morphing internals. After upgrading Livewire, manually smoke-test one edit form: load with existing content, type mid-document, save, reload. Don't rely on the CI matrix as proof for your layout — the package tests its component, not your surrounding markup.
  • PHP 8.5 = Livewire 4 only. The matrix is explicit: 8.5 is only validated with Livewire 4. Don't mix 8.5 with Livewire 3 and expect this package's support story to cover you.
  • Large documents are slow for reasons outside the editor. A 200 KB HTML payload goes over the wire on every round trip. Cap content length in validation (max:65535 on a longText is fine; a text column truncating silently is not) and avoid .live binding on long-form fields.
  • Validation messages don't appear automatically. The editor component doesn't render its own @error block in every version — add @error('content') in your own Blade around the component.
  • Toolbar customisation is config, not a component prop. If you find yourself passing a toolbar array as a parameter and it's ignored, check the published config file; that's the supported extension point.
  • Empty state is '<p><br></p>', not ''. Quill normalises empty content. Your required rule may pass on a visually empty editor — normalise/trim-and-strip-tags before validating if an empty body must be rejected.
  • Pinning: if you must stay on Livewire 3 for other dependencies, ^5.4 is still safe (constraint is ^3.0|^4.0), but pin consciously — future releases may not maintain the 3.x leg, and this release is the moment that risk becomes visible in the CI matrix.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox