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 Field Laravel Package

backpack/ckeditor-field

A Laravel Backpack field that adds CKEditor rich text editing to admin forms, with configurable toolbars, file uploads, and straightforward HTML content storage for Backpack CRUD interfaces.

View on GitHub
Deep Wiki
Context7

Getting Started

The backpack/ckeditor-field package is a Backpack for Laravel CRUD field type that replaces a plain textarea with a rich-text CKEditor instance. It is not a standalone editor wrapper — it is specifically a Backpack field, so it assumes backpack/crud is already installed and working in an admin panel.

Install:

composer require backpack/ckeditor-field

The package ships its own service provider (auto-discovered) and JS/CSS assets. No publish step is required for a first run, but you will likely want to publish the config (see below) if the version you install ships one.

First use case — swap a textarea for rich text in a CRUD:

// app/Http/Controllers/Admin/ArticleCrudController.php

protected function setupCreateOperation()
{
    CRUD::field('title');
    CRUD::field('content')->type('ckeditor');
}

protected function setupUpdateOperation()
{
    $this->setupCreateOperation();
}

Store the column as text/longText in the database. On the model side, decide whether the attribute is trusted HTML ($casts untouched) or must be sanitized on save/render — see the Gotchas section, because this is the single biggest correctness issue with rich-text fields.

Where to look first:

  1. vendor/backpack/ckeditor-field/README.md — toolbar presets, options array, upload config.
  2. vendor/backpack/ckeditor-field/src/ — the field class and default options merge logic; this tells you exactly what keys the field accepts.
  3. vendor/backpack/ckeditor-field/resources/views/fields/ckeditor.blade.php — the view that renders the <textarea> and the JS init call. If the editor "doesn't appear," this is where the mismatch is.
  4. vendor/backpack/ckeditor-field/public/ — the bundled CKEditor build; check whether it is CKEditor 4 or 5 before you build features on top of it (see Gotchas).

Verify it works before customizing: open a create form, confirm the toolbar renders, type formatted content, save, and confirm the HTML round-trips. If the toolbar does not render, you almost always have an asset-loading problem in config/backpack/base.php or a stale php artisan backpack:install state — not a field-definition problem.

Implementation Patterns

Toolbar presets and per-field overrides. The field accepts a toolbar selector and an options array that is merged into the editor config. Prefer per-field configuration over global config so different entities can have different capabilities:

CRUD::field('body')
    ->type('ckeditor')
    ->options([
        'toolbar' => 'full',       // 'minimal' | 'basic' | 'standard' | 'full' | custom array
        'height'  => 400,
    ]);

For a restricted editor (e.g. comments, no images, no source button), pass an explicit toolbar array rather than a named preset. It is more verbose but survives package upgrades that change what simple/full mean.

Factor field definitions into a trait or helper. Backpack controllers get noisy fast. A common pattern:

trait HasRichTextFields
{
    protected function addRichTextFields(array $names): void
    {
        foreach ($names as $name) {
            CRUD::field($name)->type('ckeditor')->options(['toolbar' => 'standard']);
        }
    }
}

This keeps create/update/inline-create operations consistent and gives you a single place to change editor behavior across the panel.

File/image uploads. CKEditor's uploader needs an endpoint that returns its expected JSON envelope ({"uploaded":1,"fileName":"x.png","url":"/storage/x.png"} or the CKEditor 5 equivalent). Two viable approaches:

  • Point the editor at a route you control, validate with a Laravel FormRequest, store via Storage::disk('public'), and return the JSON payload.
  • Reuse Backpack's existing upload endpoint if you also have a browse/image field configured — but only if the response shape matches; do not assume it does.

Register the URL through the field's options (e.g. filebrowserUploadUrl on CKEditor 4 builds). Keep upload authorization in the route middleware, not in JS — the editor will happily POST anywhere.

Pair with a plain-text companion column for search/listing. Rendering HTML in a Backpack list column is awkward and full-text searching HTML is worse. A common pattern:

  • content (longText, HTML) — edited with ckeditor.
  • excerpt (text) — populated by a model observer or saving hook via Str::limit(strip_tags($content)), used in list columns and search.

Conditional and layout integration. Because it is a normal Backpack field, everything composes: ->tab('Content'), ->hint(...), ->wrapperAttributes([...]), ->attributes(['rows' => 10]) (the textarea fallback), and ->type('ckeditor') inside repeaters. It also respects ->fake() and ->default() like any other field, which is handy for seeding an editor with a template.

Translations. With spatie/laravel-translatable, use backpack/pro's translatable field wrapper, or define one ckeditor field per locale. Do not rely on a single editor holding JSON-encoded translations — editors mangle JSON.

Blade output. Render trusted HTML with {!! $article->content !!} and nothing else. Anywhere the content is untrusted or user-submitted by lower-privilege roles, sanitize:

{!! clean($article->content) !!}   {{-- mews/purifier --}}

Testing. Feature tests should just post the HTML body:

$this->actingAs($admin)->post(backpack_url('article'), [
    'title' => 'Test',
    'content' => '<p>Hello <strong>world</strong></p>',
])->assertRedirect();

If you enforce sanitization, assert the stored value is the sanitized string, not the raw input.

Gotchas and Tips

The bundled editor version and its license/EOL status matter. Determine which major CKEditor the installed release ships. CKEditor 4 reached end-of-life in 2023 — if the package version you pull in still bundles CKEditor 4, you are shipping an unmaintained editor with known XSS history, and the license (GPL/LGPL/commercial) needs a decision in a commercial product. Check vendor/backpack/ckeditor-field/public/ and the package.json/composer constraints before committing to the package. If your organization cannot accept CKEditor 4, budget for a custom field wrapping CKEditor 5 or TinyMCE instead — the Backpack field is thin enough that this is a day of work, not a rewrite.

XSS is your responsibility, not the field's. The field only renders and stores whatever the editor produces; it does not sanitize on save or output. Any role that can submit content can, if the toolbar allows it, inject <script> or onerror= payloads by switching to source mode. Do at least one of: strip disallowed tags on save with mews/purifier or symfony/html-sanitizer, or sanitize on output in Blade. Do it before you have untrusted editors, not after.

"Editor doesn't load" is almost always an asset problem. Symptoms: a plain textarea with no toolbar, or a console error about CKEDITOR is not defined. Debug in this order: (1) browser console for 404s on ckeditor.js; (2) php artisan backpack:install / asset publish was skipped after install; (3) you are on a Backpack major version the installed field version does not support — check the package's composer.json backpack/crud constraint against your composer.lock; (4) a CSP header blocking inline scripts, which CKEditor init relies on.

Repeaters and dynamically added rows re-render the textarea without the editor. The init runs once on page load. For fields inside repeater or any UI that clones DOM nodes, you need to re-initialize on the repeater's row-added event, or the row shows a bare textarea. Same problem with any Alpine/Livewire re-render in Backpack 6 views. Look at the package's view for the exact init function name and call it yourself from a document listener.

options merge order is easy to get wrong. Per-field options typically override the package defaults, but not always — verify in the source whether your array replaces or is deep-merged. If your toolbar change silently has no effect, dump the rendered init script (view source in the browser) and confirm your value actually made it into the CKEDITOR.replace(...) payload.

Config caching after install. If the package registers a config file and you edited it, run php artisan config:clear locally and php artisan config:cache on deploy. A cached config from before the install is a classic "why is my toolbar ignored" cause.

Content in list columns. Do not set the list column to type => 'text' and hope; Backpack will render escaped tags and look broken, or unescaped HTML and look worse. Use a stripped excerpt column, or type => 'text' with an accessor that strips tags, or a custom column view with Str::limit(strip_tags($value)).

Empty editor = <p></p>, not empty string. After saving and re-opening a "blank" record, validation rules like required and min:1 will pass on a document containing only <p><br></p>. If empty content must be rejected, normalize on save in a saving model event or FormRequest::prepareForValidation().

Where to extend. The clean extension points are: a custom field class extending the package's, a published/blade-overridden field view (php artisan backpack:publish --tag=views style or a local resources/views/vendor/backpack/... override, depending on version), and a custom upload route. Avoid editing vendor/ — a composer update on Backpack will wipe it, and this package moves with Backpack majors.

Version pinning. Since the field trails Backpack versions, pin both in composer.json together and upgrade them as a pair. A backpack/crud major bump with an older ckeditor-field is a common source of "field type not found" errors (Backpack\CRUD\app\Library\CrudPanel\CrudPanel::addField(): Unknown field type 'ckeditor').

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/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
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route