kms/froala-editor-bundle
A Laravel bundle that integrates the Froala WYSIWYG editor into your application, providing a ready-made rich text editing field with its assets, configuration, and framework-friendly setup for quickly adding Froala to forms and content management screens.
Reality check first: this is a Symfony bundle, not a Laravel package. There is no service provider, no facade, no config publish tag. In a Laravel app you will not install this — you install froala-editor from npm and replicate the ~80 lines of glue this bundle provides. The value of reading it is that it shows you the canonical wiring (options merging, per-profile config, form integration, upload endpoints) so you can copy the shape into a Blade component + a small FroalaOptions service.
For a Symfony app, the minimal path:
composer require kms/froala-editor-bundle
Register KMS\FroalaEditorBundle\KMSFroalaEditorBundle in config/bundles.php (Flex does this for you), then the smallest useful config:
# config/packages/kms_froala_editor.yaml
kms_froala_editor:
language: en
height: 300
toolbar: [bold, italic, underline, "|", insertLink, insertImage, "|", html]
Use it as a form field — this is the 90% use case:
use KMS\FroalaEditorBundle\Form\Type\FroalaEditorType;
$builder->add('body', FroalaEditorType::class);
Or standalone in Twig against your own <textarea id="body">:
{{ froala_editor() }}
Where to look first, in this order:
README.md — thin, but has the config sample and the form/Twig examples.DependencyInjection/Configuration.php — the authoritative list of every YAML key. This is the real documentation.Service/OptionManager.php — how YAML keys become the JS options object, including defaults you didn't ask for.Form/Type/FroalaEditorType.php — the form-level options (profile, froala_options, attr).First use case to prove it works: an Article form with a body field, save the HTML, render it back with |raw after sanitizing. If that round-trips without the toolbar disappearing or the content being HTML-escaped twice, the integration is sound.
Named profiles instead of per-field option arrays. Define the two or three editor shapes you actually need once in YAML, then reference them. This keeps options out of PHP and makes them editable without a deploy review of form classes.
kms_froala_editor:
profiles:
default:
height: 300
toolbar: [bold, italic, insertLink]
full:
height: 500
toolbar: [bold, italic, underline, insertTable, insertImage, html]
imageUploadURL: /admin/upload/image
$builder->add('body', FroalaEditorType::class, ['profile' => 'full']);
Configurator service for anything dynamic. The bundle's real extension point is ConfiguratorInterface: a service that receives the resolved options before render, so you can vary the toolbar by role, inject a signed upload URL, or flip toolbarInline for a specific entity. Wire it via the configurator config key. Use this rather than sprinkling froala_options arrays across form types — one place to reason about, one place to test.
Upload endpoints are yours to write, and they are the security boundary. Froala POSTs the file and expects {"link": "https://..."} back. In Symfony that's a controller taking Request $request and UploadedFile; in Laravel an invokable controller with a FormRequest. Non-negotiables: validate by extension and re-encode images, generate random filenames, store outside the public root or in a dedicated disk, and return only the public URL. Do not pass the user's filename through. Bind the endpoint behind your admin middleware — an open imageUploadURL is an unauthenticated file-upload primitive.
Sanitize on write, not on read. Store clean HTML in the column. Use HTMLPurifier (ezyang/htmlpurifier, or mews/purifier in Laravel) with a whitelist covering only the tags your toolbar can produce. Then {{ article.body|raw }} (Twig) / {!! $article->body !!} (Blade) is a defensible render. If you skip this, one paste from Word carries <style> blocks and onerror attributes straight into your database.
Multiple editors / dynamic forms. Each rendered textarea needs its own new FroalaEditor('#id', opts) call. For Symfony collection types or Alpine/Livewire-driven "add another block" UIs, the initializer runs once on DOM ready — you must re-init manually after inserting nodes. Keep a per-page registry of instances and destroy them on removal; otherwise you accumulate orphaned toolbars.
Laravel translation of the pattern: npm i froala-editor, import CSS/JS in resources/js/app.js, build a <x-froala name="body" profile="full" /> Blade component that renders a textarea plus an inline new FroalaEditor call sourced from a config/froala.php array shaped identically to the bundle's YAML. Same mental model, none of the bundle.
useCDN is on, the bundle loads whatever version its asset URL references. Mixing that with locally installed plugins from a different Froala major is the classic "plugin silently does nothing" bug. Pick one source of truth for assets."|" must be quoted or YAML treats it as a block scalar and your toolbar becomes one giant string. Same for any option expecting a literal pipe.debug:config is your friend. Config is compiled; after editing YAML run bin/console cache:clear and bin/console debug:config kms_froala_editor to see the merged result rather than guessing why an option had no effect. Then inspect the inline <script> in the rendered page — that is the actual options object Froala received.fetch() posts bypass that. Call the editor's HTML getter explicitly before sending.turbo:before-cache (or your router's equivalent) or you get flickering duplicated toolbars and leaked listeners.enter, paste-as-text where appropriate) rather than relying on defaults, and keep the sanitizer whitelist in sync with the toolbar you expose.composer.json, assume you may need to fork it, and don't build your editor abstraction so tightly around its form type that swapping to a hand-rolled Twig include is painful. The abstraction you actually want is "my options array + my upload endpoint," and both are portable.How can I help you explore Laravel packages today?