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

Ckeditor Bundle Laravel Package

fsi/ckeditor-bundle

The FSI CKEditor Bundle integrates the CKEditor WYSIWYG editor into Symfony applications, providing a configurable form type, toolbar and plugin options, asset/configuration handling, and simple embedding for rich-text content editing.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the bundle through Composer and register it. It's a Symfony bundle wrapper around CKEditor 4, so the bulk of "getting started" is wiring config and assets — not writing editor code.

composer require fsi/ckeditor-bundle

Traditional Symfony (3.x / 4.0–4.2) registration:

// app/AppKernel.php
public function registerBundles()
{
    return [
        // ...
        new Ivory\CKEditorBundle\IvoryCKEditorBundle(),
    ];
}

With Flex, the bundle is auto-discovered; put configuration in config/packages/ivory_ck_editor.yaml.

Minimal configuration — one named config you'll reuse everywhere:

# config/packages/ivory_ck_editor.yaml
ivory_ck_editor:
    default_config: my_config
    render_assets: true
    configs:
        my_config:
            toolbar:
                - [Bold, Italic, '|', Link, Unlink]
                - [NumberedList, BulletedList, '|', Undo, Redo]
            uiColor: '#f4f4f4'
            height: 300

Make sure the CKEditor library is available where the bundle expects it (typically public/bundles/ivoryckeditor/ in Symfony 4+, web/bundles/... in Symfony 3), then run bin/console assets:install.

First use case — a form field. This is 95% of real usage:

use Ivory\CKEditorBundle\Form\Type\CKEditorType;

$builder->add('body', CKEditorType::class, [
    'config_name' => 'my_config',
]);

Render the assets once in your base layout (usually before </body>):

{{ ckeditor_assets() }}

Then let the form type do the rest: {{ form_row(form.body) }} automatically emits the textarea plus the inline replacement script.

If you're not using Symfony Forms, the Twig helpers cover the rest:

{# textarea + replace script #}
{{ ckeditor_widget('my_config') }}

{# or replace existing elements by selector #}
{{ ckeditor_replace('my_config', {'selector': '.rich-text'}) }}

First thing to check when it doesn't work: whether the CKEditor core JS is actually reachable at the configured base_path/js_path, and whether ckeditor_assets() is rendered exactly once.

Implementation Patterns

Config-driven toolbar variations. Rather than stuffing toolbar arrays into every form type call, define named configs in YAML (basic, full, comment) and reference them:

ivory_ck_editor:
    configs:
        basic:
            toolbar: [[Bold, Italic, Link]]
        full:
            toolbar:
                - [Bold, Italic, Underline, '|', NumberedList, BulletedList]
                - [Link, Unlink, Image, Table, '|', Source]
            allowedContent: true

That keeps the toolbar consistent across admin and frontend, and makes it trivial to swap in tests.

Per-form overrides when you need one-off behavior — the config option merges over the named config:

$builder->add('footer', CKEditorType::class, [
    'config_name' => 'basic',
    'config'      => ['height' => 120, 'toolbar' => [['Bold', 'Italic']]],
]);

Plugin registration goes through a plugins section, then extraPlugins on the config that wants them:

ivory_ck_editor:
    plugins:
        wordcount:
            path: bundles/ivoryckeditor/plugins/wordcount/
            filename: plugin.js
    configs:
        full:
            extraPlugins: 'wordcount'

File uploads / filebrowser — the bundle does not ship a file browser. The common pattern is to enable the filebrowser* options on the config (which just sets CKEditor's URL properties) and point them at your own controller:

configs:
    full:
        filebrowserUploadUrl: '/admin/ckeditor/upload'
        filebrowserBrowseUrl: '/admin/ckeditor/browse'

Your controller is responsible for validating MIME types, size, and returning a CKEditor-compatible JSON response.

Twig-only pages (static content editors, snippets, email templates) are often cleaner than dragging Symfony Forms in:

<form method="post">
    <textarea name="content">{{ content }}</textarea>
    {{ ckeditor_replace('full', {'selector': 'textarea[name=content]'}) }}
    <button type="submit">Save</button>
</form>

Re-rendering after AJAX is a classic gotcha. When you inject a form fragment via AJAX, the inline CKEDITOR.replace() script won't re-run. The usual pattern is to return the rendered HTML and explicitly call CKEDITOR.replace in the success handler using config exported from the bundle, or use ckeditor_replace() inside the returned fragment.

Testing. Because CKEditorType is just a TextareaType subclass with attributes, functional tests work normally — the rendered output is a <textarea> with data-* attributes. Assert on the textarea value rather than on CKEditor UI.

Gotchas and Tips

This is effectively unmaintained. Last release is 2018, it wraps CKEditor 4 (which is EOL), and it targets old Symfony versions. On Symfony 5+, expect dependency conflicts (symfony/form version constraints). Treat it as a stopgap: plan a migration to CKEditor 5 with a dedicated Symfony integration, or to a maintained alternative. If you must use it, pin versions aggressively and check the composer constraints before upgrading Symfony.

Sanitize on the server. CKEditor output is raw HTML. Escaping with |raw in Twig without sanitizing is an XSS hole — especially with allowedContent: true. Run content through HTMLPurifier (or a whitelist sanitizer) on save, not just on render.

Render assets exactly once. Calling ckeditor_assets() in multiple layouts (base + child) produces duplicate CKEditor loads; symptoms include CKEDITOR is not defined, toolbar rendering twice, or silent failures. Guard it with a block or render only in the base layout.

Asset paths break after upgrades. base_path / js_path / css_path defaults assume the old web/bundles/ layout. In Symfony 4+ you need public/bundles/ivoryckeditor/, and in Symfony 5+ with asset versioning/CDN, the paths must be absolute or go through the asset mapper. This is the number-one cause of "blank editor" issues.

Config merge semantics are shallow-ish. Arrays merge, scalars override. If you set toolbar in both the named config and the form option, the form option wins entirely — not element-by-element. Debug with bin/console config:dump-reference ivory_ck_editor.

required and empty content. The form field is required by default; an empty CKEditor produces <p><br></p> in some configurations, which passes validation as non-empty. Either normalize empty editor HTML server-side or use a NotBlank constraint aware of the placeholder.

File uploads are yours to secure. The filebrowser* config options are just URL hooks — no upload handling exists. Don't blindly trust CKEditorFuncNum or the type parameter; validate against an allowlist and never write to a public directory without sanitizing the filename.

Debugging workflow. When the editor doesn't initialize: (1) view source — is the inline CKEDITOR.replace(...) script emitted? (2) check the browser console for a 404 on ckeditor.js; (3) confirm the textarea id matches the script's selector; (4) verify your configs key matches config_name — a typo silently falls back to the default config, which is why "my toolbar changes do nothing."

Extension point for custom form types. If you need a project-wide default (e.g. always add wordcount), create a custom CKEditorType subclass or a form type extension and register it — far cleaner than repeating config arrays across every form.

Watch for Sonata/EasyAdmin conflicts. Both admin bundles ship their own CKEditor integrations; registering this bundle alongside them often causes two copies of CKEditor on the same page and duplicated assets. Pick one integration per project.

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.
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource