sirroland/ckeditor-bundle
A Symfony bundle that integrates the CKEditor rich text editor into your application, making it easy to add and configure WYSIWYG fields in forms and templates. It packages CKEditor assets and setup so you can enable feature-rich HTML editing with minimal configuration.
First, a scope check. This is a Symfony bundle (Ivory\CKEditorBundle), not a Laravel package. There is no service provider, no facade, and no config/ckeditor.php. If you are working in a Laravel app you cannot composer require this and expect anything to happen — you want unisharp/laravel-ckeditor, a Livewire editor component, or just a CKEditor 5 npm install and a Blade partial. The rest of this write-up assumes you have been dropped into a Symfony project (or are evaluating this bundle before recommending a replacement).
Also note the package is effectively a fork/mirror of the long-abandoned Ivory CKEditor bundle, last tagged in January 2019. Before writing code against it, open composer.json in the repo and confirm which Symfony versions the constraints actually allow. On modern Symfony (5.4+/6+) you will almost certainly need friendsofsymfony/ckeditor-bundle (the maintained successor) instead — the API below is ~95% identical, so anything you learn here transfers directly.
Minimal path in a compatible Symfony 3.4/4.x app:
Read the bundle class first. vendor/sirroland/ckeditor-bundle/ — confirm the namespace (Ivory\CKEditorBundle vs a fork-specific namespace) and the container alias. This determines your config root key (ivory_ck_editor: is the historical one).
Install and register:
composer require sirroland/ckeditor-bundle
// config/bundles.php
Ivory\CKEditorBundle\IvoryCKEditorBundle::class => ['all' => true],
Drop in a config file — config/packages/ivory_ck_editor.yaml:
ivory_ck_editor:
default_config: default
autoload: true # let the bundle ship CKEditor's JS for you
configs:
default:
toolbar: standard
First use case — a form field:
use Ivory\CKEditorBundle\Form\Type\CKEditorType;
$builder->add('body', CKEditorType::class);
Render normally with {{ form_row(form.body) }}. The bundle emits the <textarea> plus an inline <script> that boots CKEditor against it.
Verify the pipeline with bin/console debug:config ivory_ck_editor (or whatever the alias is) and bin/console debug:container --tag=form.type | grep -i ckeditor. If the type is not registered, nothing else matters.
Named configs, one per editor context. The bundle's main value is that you define CKEditor configs once in YAML and reference them by name from any form across the app. Keep a small set (minimal, standard, full, comment) rather than per-form one-offs:
ivory_ck_editor:
default_config: standard
configs:
minimal:
toolbar: [['Bold', 'Italic']]
height: 120
standard:
toolbar: standard
uiColor: '#f4f4f4'
$builder->add('comment', CKEditorType::class, ['config_name' => 'minimal']);
Per-field config overrides. The config option merges a raw array on top of the named config — useful for one-off height or read-only tweaks without polluting the YAML:
$builder->add('notes', CKEditorType::class, [
'config_name' => 'standard',
'config' => ['height' => 300, 'readOnly' => true],
]);
File uploads / elFinder. The classic Symfony integration is config.filebrowserUploadRoute + filebrowserBrowseRoute pointing at a controller (often the FMElfinderBundle). Wire it once in the config:
standard:
filebrowserUploadRoute: admin_upload
filebrowserBrowseRoute: admin_browse
Then secure those routes — see the gotcha below.
Asset handling. autoload: true is the fast path and is fine for prototypes. In a real app using Webpack Encore, set autoload: false, install CKEditor via npm, and import ckeditor/ckeditor in your entrypoint. The bundle still handles the form type; you just own the script tag.
Reusable application form type. Wrap CKEditorType in your own type (RichTextType) so the rest of the codebase never imports a third-party bundle form type directly. When you eventually swap to FOSCKEditor or CKEditor 5, you change one class:
class RichTextType extends CKEditorType
{
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefault('config_name', 'standard');
}
}
Non-form usage. Rendering an editor outside a Symfony Form is painful with this bundle — it is form-centric. For Twig-only pages, either create a tiny one-field form in the controller or bypass the bundle and call CKEditor's ClassicEditor.create() yourself. Don't fight it.
Testing. Because CKEditorType extends TextareaType, PHPUnit form tests keep working — assert on the <textarea name="..."> and the presence of the CKEditor config JSON in the response. No browser needed for the form contract.
Laravel equivalent patterns, if you're translating: laravel-ckeditor gives you @ckeditor('body') in Blade; for a richer setup (uploads, plugins) you'll find yourself writing the controller endpoint and CKEditor config object by hand regardless of framework — the JavaScript side is identical. The only thing this bundle saves you is the Symfony form wiring.
friendsofsymfony/ckeditor-bundle or CKEditor 5 directly.ivory_ck_editor, egeloen_ck_editor (older), or fork-specific. Always resolve the truth with bin/console debug:config / config:dump-reference before assuming the YAML key — a wrong root key silently does nothing rather than erroring loudly in some versions.autoload: true vs Encore is a real conflict. If you enable autoload and import CKEditor in your Webpack entrypoint, you get two copies and confusing "editor already attached" behavior. Pick one. The bundle's autoload is also pinned to whatever CKEditor 4.x it vendored years ago — you don't get CKEditor 5 features.toolbar.items, @ckeditor/* plugins) will not work in this bundle's YAML. Use the legacy toolbar: [['Bold','Italic']] array syntax.config merges shallowly. Nested arrays (toolbar groups, plugin lists) are replaced wholesale, not deep-merged. If your override unexpectedly drops a toolbar row, that's why. Put the full desired value in the override.filebrowserUploadRoute calls a controller with a file — make sure that route is behind auth, validates MIME/extension server-side, and does not trust CKEditorFuncNum. The bundle hands the URL and func-num to the browser; validation is entirely your job.debug:container --tag=form.type; (2) is the config alias correct — debug:config; (3) is the JS actually loaded — view source and search for ckeditor.js; (4) did the inline init script run — check the console for CKEDITOR is not defined (asset problem) vs editor already attached (double bootstrap).ivory_ck_editor.config_manager (or similarly named) is a public service you can use to read/modify configs at runtime; the form type accepts config so most customization happens there rather than via events. If you need plugin-specific Symfony wiring, a small custom form type wrapping CKEditorType is cleaner than monkey-patching the bundle.data-controller hook for bootstrapping, and a plain controller for uploads. You will write less glue than this bundle provides.How can I help you explore Laravel packages today?