ehyiah/ux-quill
Symfony UX bundle integrating the Quill.js WYSIWYG editor. Add QuillType to your forms (works well with EasyAdmin), supports AssetMapper or Webpack Encore builds, and includes simple Twig patterns to render saved HTML with Quill styling.
Install the package:
composer require ehyiah/ux-quill
For AssetMapper users, no further steps are needed. For Webpack Encore, run:
yarn install --force && yarn watch
or
npm install --force && npm run watch
First use case: Add a WYSIWYG field to a Symfony form.
// src/Form/PostType.php
use Ehyiah\QuillJsBundle\Form\QuillType;
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder->add('content', QuillType::class);
}
Render the content in Twig:
{{ quill_content_styles() }} {# Required for AssetMapper #}
<twig:QuillContent value="{{ post.content }}" />
For inline styling (no Quill CSS):
<twig:QuillContent value="{{ post.content }}" style="inline" />
$builder->add('bio', QuillType::class, [
'label' => 'About Me',
'placeholder' => 'Write your story...',
]);
$builder->add('description', QuillType::class, [
'quill_options' => [
'toolbar' => [
['bold', 'italic'], // Groups
['clean'], // Separator
['link', 'image'], // Fields
],
],
]);
$builder->add('terms', QuillType::class, [
'readonly' => true,
]);
// src/Entity/PostCrudController.php
use Ehyiah\QuillJsBundle\Form\QuillAdminField;
public function configureFields(string $pageName): iterable {
yield QuillAdminField::new('content')
->setLabel('Article Content')
->setQuillOptions([
'modules' => ['history', 'toolbar'],
]);
}
<div class="ql-snow">
<div class="ql-editor">
{{ post.content|raw }}
</div>
</div>
<div>{{ post.content|raw }}</div>
Extend Quill behavior via Stimulus controllers:
// assets/controllers/quill_controller.js
import { Controller } from '@hotwired/stimulus';
export default class extends Controller {
connect() {
this.application.register('quill', QuillJsBundle.QuillController);
this.quill.on('text-change', () => {
console.log('Content changed!');
});
}
}
PHP Configuration:
$builder->add('notes', QuillType::class, [
'quill_controller' => 'custom-quill',
]);
Configure a custom upload endpoint:
$builder->add('article', QuillType::class, [
'upload_endpoint' => '/api/upload',
'upload_options' => [
'headers' => ['Authorization' => 'Bearer token123'],
],
]);
Backend Route (Symfony):
# config/routes.yaml
api_upload:
path: /api/upload
controller: App\Controller\UploadController::upload
methods: POST
$builder->add('editor', QuillType::class, [
'modules' => ['history', 'markdown', 'imageGallery'],
]);
ReadingTime):
use Ehyiah\QuillJsBundle\Form\QuillFieldModuleInterface;
class ReadingTimeModule implements QuillFieldModuleInterface {
public function getName(): string { return 'readingTime'; }
public function getConfig(): array { return []; }
}
Register in QuillType options:
$builder->add('content', QuillType::class, [
'modules' => [new ReadingTimeModule()],
]);
AssetMapper vs. Webpack Encore:
quill_content_styles() in Twig.yarn watch is running. Import Quill CSS in your entrypoint:
// assets/app.js
import 'quill/dist/quill.snow.css';
Sanitization: Use Symfony’s built-in sanitizer for form data:
// src/Controller/PostController.php
use Symfony\Component\HtmlSanitizer\SanitizerInterface;
public function update(Request $request, SanitizerInterface $sanitizer) {
$data = $request->request->all();
$data['content'] = $sanitizer->sanitize($data['content']);
// ...
}
Live Components: For Turbo/Stimulus Live Components, ensure the Quill instance is reinitialized:
// assets/controllers/quill_live_controller.js
import { Controller } from '@hotwired/stimulus';
export default class extends Controller {
connect() {
this.quill = QuillJsBundle.QuillController.init(this.element);
}
disconnect() {
this.quill.quill?.destroy();
}
}
EasyAdmin Asset Mapping:
Add Quill assets to EasyAdmin’s AssetMapper:
// src/Admin/AppAdmin.php
public static function getAssetMapper(): AssetMapper {
$assetMapper = parent::getAssetMapper();
$assetMapper->addEntry('quill-css', 'bundles/quilljs/dist/quill.snow.css');
return $assetMapper;
}
Table Module Issues:
$post->setContent('<table>...</table>');
Asset Loading Race Conditions:
data-controller="quill" on the form field and ensure quill_content_styles() is called in the parent template.Sanitization Conflicts:
sanitize_html in QuillType options and rely on Symfony’s default sanitizer:
$builder->add('content', QuillType::class, [
'sanitize_html' => false, // Let Symfony handle it
]);
EasyAdmin Styling:
!important in your CSS or override EasyAdmin’s styles:
/* assets/css/easyadmin.css */
.easyadmin-field-quill .ql-snow {
all: initial !important;
}
Module Initialization Order:
hydrate:before event to inject modules:
// assets/controllers/quill_controller.js
connect() {
this.quill.on('hydrate:before', (quill) => {
quill.register('modules/readingTime', ReadingTimeModule);
});
}
Turbo/Stimulus Conflicts:
data-turbo-permanent on the form or reinitialize in turbo:load:
document.addEventListener('turbo:load', () => {
QuillJsBundle.QuillController.initAll();
});
// assets/app.js
import { Application } from '@hot
How can I help you explore Laravel packages today?