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

Nova Ckeditor5 Classic Laravel Package

numaxlab/nova-ckeditor5-classic

Integrate the CKEditor 5 Classic rich-text editor into Laravel Nova as a ready-to-use field. Provides a familiar WYSIWYG editing experience for content fields in Nova resources and forms.

View on GitHub
Deep Wiki
Context7

Getting Started

Check the version constraint before anything else. Last release was 2021-01-12, so this is a Nova 3-era field. Run composer show numaxlab/nova-ckeditor5-classic --all and read its laravel/nova requirement. On Nova 4/5 you will almost certainly hit a resolution conflict — treat adoption as a small maintenance project (relax the constraint, then verify the field's Nova::script/Nova::style registration still works) rather than a drop-in.

  1. composer require numaxlab/nova-ckeditor5-classic
  2. php artisan vendor:publish (select the package's provider/tag) and, if needed, php artisan nova:publish. Confirm public/vendor/nova-ckeditor5-classic/ exists and the JS/CSS appear in the browser network tab when a form loads.
  3. Open src/ and read the Field class — it will be something like Numaxlab\NovaCKEditor5Classic\CKEditor5Classic. That file, plus the built bundle in dist/ (or resources/js/field.js), is where the toolbar, plugin list, and upload config live. Those three things are what you'll actually need to change.
  4. Create the column as longText — CKEditor emits full HTML with attributes; a MySQL TEXT column (64 KB) is exceeded by a medium-length article with pasted formatting.

First real use case — long-form article body:

use Numaxlab\NovaCKEditor5Classic\CKEditor5Classic;

CKEditor5Classic::make('Body')
    ->rules('required', 'max:65535')
    ->hideFromIndex();

Verify a round-trip: create → save → reopen → confirm the toolbar state and content survive without the editor double-instantiating.

Implementation Patterns

Wrap the field in your own class. Because the package is archived, don't sprinkle CKEditor5Classic::make() across resources — subclass it once in app/Nova/Fields/BodyEditor.php and have every resource call your class. When you eventually fork or swap to a maintained CKEditor field, you change one file.

Configure via metadata, not by editing vendor files. Pass toolbar/height/plugin settings through ->withMeta([...]) and have field.js read this.field props at editor-init time. Anything you hard-code in dist/js/field.js is lost the next time someone runs vendor:publish --force.

Sanitize server-side. The editor constrains what authors can type, not what's in the request. Pair the field with a purifier (mews/purifier) as an Eloquent mutator or an observer on save, with an explicit allowlist (p, a[href], ul, ol, strong, em, h2, h3, blockquote, figure, img[src]). This also protects you if the bundled CKEditor build has a stale paste-from-Word or HTML-embed path.

Display deliberately. On detail views, render raw HTML only after sanitization (->asHtml() or {!! $model->body !!}); on index views, truncate hard:

->displayUsing(fn ($value) => Str::limit(strip_tags($value ?? ''), 80))

Custom build when the default toolbar isn't enough. The bundled bundle is a frozen CKEditor 5 Classic build. The durable pattern is: publish the package JS, add @ckeditor/ckeditor5-build-classic and the plugins you need as dev dependencies in your app, edit resources/js/field.js to import your configured build, and npm run prod. Commit the compiled asset. Never rely on the package's dist/ after that.

Locale fan-out. For multi-language content, one field per locale (Body (EN), Body (FR)) bound to body->en via ->resolveUsing(). Don't try to run one editor instance across locales.

Image handling. Out of the box this is text-only unless an upload adapter is wired up. If you need images, the usual integration is an adapter that POSTs to a Nova controller and returns a URL, or defer to a dedicated media field (Spatie Media Library) plus a CKEditor plugin pointing at those URLs. Falling back to base64-embedded images "works" but silently multiplies row size and fights your longText/max: rules.

Gotchas and Tips

  • Archived means no fixes. Old CKEditor 5 builds have known XSS advisories in paste/source-editing handling. If untrusted or semi-trusted users author content, sanitize on the server — do not trust the editor's output.
  • Licensing. CKEditor 5 is GPL-2.0-or-later or commercial. Bundling it in a closed-source SaaS is the kind of thing that gets flagged in an audit; confirm your usage or buy a license from CKSource.
  • Stale assets after upgrade. The classic failure mode is "field renders as an empty textarea" or "editor is unstyled." Cause is almost always a stale published JS/CSS. Re-run the publish step with --force and hard-refresh; check that mix-manifest/asset paths in the package don't collide with your own nova:publish output.
  • Validation vs. storage mismatch. ->rules('max:65535') protects the column, but PHP's max rule counts characters while MySQL's TEXT limit counts bytes — UTF-8 content with emoji or CJK text can pass validation and still truncate. Prefer longText (4 GB) and stop worrying.
  • Empty content is not empty. Clicking into the editor and back out can leave <p>&nbsp;</p> in the payload, which defeats required. Add a prepareForValidation-style normalizer (or a Nova fillUsing that strips empty paragraph tags) if required needs to actually mean something.
  • Reactivity. Setting the field value programmatically after mount (defaults from another field, dependent selects) won't update the editor unless field.js explicitly watches the value prop and calls editor.setData() with a guard against feedback loops. This is the most common thing you'll have to patch.
  • Multiple instances on one form. Two editors on the same resource work, but duplicate id attributes or a shared editorInstance singleton variable will break them. Check field.js for a module-level editor reference if only the first editor initializes.
  • Vapor / CDN deployments. Assets live under public/vendor/; if you deploy with Vapor or serve assets from S3, ensure that path is included in your asset pipeline, otherwise the field silently degrades to a plain textarea in production but works locally.
  • No dark-mode or Nova 4 theming. Expect the iframe/toolbar to look off against a custom Nova theme; budget a small CSS override in your app's nova.css rather than patching the package.
  • Extension point worth knowing. The field likely exposes ->withMeta(['options' => [...]]) — dump this.field in the browser console once and you'll have the whole surface area. That, plus the upload adapter hook, is effectively the entire API; there's nothing else to learn in the repo.
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.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
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