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

Ckeditor5 Symfony Laravel Package

mati365/ckeditor5-symfony

Symfony bundle that integrates CKEditor 5 into your forms. Register a ready-made form type, load the editor assets, and configure uploads or plugins with minimal setup—letting you add a modern rich text editor to any Symfony application quickly.

View on GitHub
Deep Wiki
Context7

Getting Started

First, calibrate expectations: mati365/ckeditor5-symfony is a Symfony bundle, not a Laravel package. There is no service provider, no config/ckeditor5.php, no Blade directive, and no facade. As a Laravel developer you'll end up in one of two modes:

  1. You're touching a Symfony codebase (client project, mono-repo, migration) — install it there and use it as designed.
  2. You're on Laravel — treat it as a reference implementation. The bundle is essentially: a config tree + a form type that renders a hidden input + a prebuilt asset installer + an upload hook. That's about a day of porting into a small Laravel package of your own.

Assuming a Symfony context:

composer require mati365/ckeditor5-symfony
php bin/console ckeditor5:install

Flex auto-registers the bundle in config/bundles.php. Then drop the form type into an existing admin form — that's the first use case and the fastest way to validate the package is healthy:

use Mati365\CKEditor5Symfony\Form\Type\CKEditor5Type;

$builder
    ->add('title')
    ->add('body', CKEditor5Type::class);
{{ form_row(form.body) }}

Where to look first: the bundle's Form/Type/CKEditor5Type.php and DependencyInjection/Configuration.php. Those two files enumerate every option the editor accepts and explain how assets are wired. Confirm the exact FQCN against the README before you paste it — small bundles rename things between versions. If your app already uses Webpack Encore or Vite, ignore ckeditor5:install entirely and point the bundle at your own build (see Gotchas).

For a Laravel equivalent, the shape is: a Blade component <x-ckeditor name="body" :value="old('body')" /> plus a Vite entry that imports ClassicEditor and mounts on a data-ckeditor attribute. Nothing more exotic than that.

Implementation Patterns

Per-form config via options. The form type maps options 1:1 onto CKEditor 5's JS config object. Keep sane defaults in one place (a service or a shared array), override per field:

$builder->add('body', CKEditor5Type::class, [
    'config' => [
        'toolbar' => ['heading', '|', 'bold', 'italic', 'link', 'bulletedList', 'numberedList', 'uploadImage'],
        'heading' => ['options' => [2, 3, 4]],
    ],
]);

Treat the editor as a textarea for validation. It submits HTML through a hidden input. Validate it like untrusted text and sanitize before persisting — paste-from-Word and image src attributes are attacker-controlled regardless of who produced the markup. In Laravel that's 'body' => 'required|string|max:65535' plus mews/purifier; in Symfony it's the equivalent constraint plus an HTML sanitizer in a form event or entity setter.

Uploads are your responsibility. The bundle renders the editor; it doesn't ship an upload endpoint you should trust blindly. Implement one:

  • Validate MIME by content, not extension. Cap size. Rename with Str::uuid(). Store outside public/ and serve through a signed route if the content is private.
  • Return the shape CKEditor expects: { "url": "..." } for SimpleUploadAdapter, or { "urls": [...] } for multi-upload adapters.
  • CKEditor's default adapter does not send your CSRF token. Either use a custom UploadAdapter in your own build, or exempt just that route from CSRF and rely on session auth. In Laravel, ->withoutMiddleware(VerifyCsrfToken::class) on that one route is the usual compromise — leave a comment explaining why.

Share the toolbar config across frameworks. In a multi-framework shop, don't duplicate the toolbar in PHP and JS. Export a JSON config once, import config from './ckeditor.config.json', and build from it on both sides. The Symfony bundle and your Laravel component then stay in sync.

Testing. In Symfony, assert on the rendered hidden input and widget class — CKEditor5Type is a form widget, not a JS test. In Laravel/Pest, unit-test the upload endpoint brutally (wrong MIME, oversized, unauthenticated, filename traversal) and use Dusk or Playwright for editor behavior. Don't try to drive CKEditor through plain HTTP feature tests; it won't run.

Livewire / Inertia / Turbo. CKEditor 5 is imperative and hates DOM morphing. On Livewire, wrap the field in wire:ignore and destroy/recreate on livewire:navigated. On Inertia, tear down in onBeforeUnmount. The same pattern applies if you ever wire the Symfony bundle into a Symfony + Turbo front end — the editor instance must be explicitly disposed before the host DOM is replaced.

Gotchas and Tips

  • Licensing is the first trap. CKEditor 5 core with the free plugins is GPL-2.0-or-later or commercial. Track changes, comments, and real-time collaboration are commercial-only. If the toolbar config includes one of those, you owe a license — check before shipping, not after.
  • 27 stars, zero dependents. Pin the exact version or a commit hash, and read the changelog on every bump. These small bundles typically bundle a specific prebuilt CKEditor 5, so a patch release can silently swap the editor version under you.
  • Asset pipeline mismatch. ckeditor5:install copies a prebuilt editor into public/. If you also have Encore or Vite building app.js, you'll load two editor copies. Pick one strategy — installer or your own npm build — and remove the other.
  • ckeditor-duplicated-modules. Almost always two CKEditor 5 builds on the page. Same for a "plugin is missing" error when the plugin is clearly in the config. Check Network for a second ckeditor.js.
  • Escaping. The stored value is HTML. Render it with {!! !!} (Blade) or |raw (Twig) deliberately, and sanitize server-side. The bundle does not sanitize on save.
  • Validation-failure re-render. The field only keeps its previous HTML if the model has it. Make sure your controller re-binds the submitted value, or users lose their draft when a different field fails validation.
  • Debugging checklist when the editor won't load: browser console → Network (404 on ckeditor.js) → bin/console debug:container | grep ckeditor → bin/console cache:clear. In a Laravel port the equivalents are the Vite build log, php artisan about, and composer show.
  • Extension points that matter: the config tree (global defaults), form type options (per-field), and the installer command (custom builds). For a custom plugin, build CKEditor 5 yourself with npm and bypass the installer — never patch public/bundles/..., it gets overwritten.
  • Laravel reality check: if the goal is "CKEditor 5 in a Laravel app," this package will not do it. Budget half a day for a Blade component, a Vite entry, and an upload route, and borrow the bundle's option shapes so a future Symfony port is copy-paste. If you need something battle-tested today, look at unisharp/laravel-ckeditor (CKEditor 4) or one of the Tiptap/Livewire editors instead — CKEditor 5 in Laravel has no clear winner.
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.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin