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

bayareawebpro/nova-field-ckeditor

CKEditor field for Laravel Nova that adds a full-featured rich text editor to resource forms. Supports image and file uploads, configurable toolbar and editor options, and seamless model storage, giving Nova users a familiar, powerful content editing experience.

View on GitHub
Deep Wiki
Context7

Getting Started

Requirements first. This field targets Laravel Nova 3 (Vue 2). It was last released 2022-02-09 and the repository is archived, so treat compatibility as a hard gate before you invest any time:

  • Laravel 8/9-era Nova 3 project, PHP 7.4–8.1
  • Nova 4+ (Vue 3) is very unlikely to work without a fork — verify in a throwaway branch, not in your main app

Install and publish:

composer require bayareawebpro/nova-field-ckeditor

php artisan vendor:publish --provider="Bayareawebpro\NovaFieldCkEditor\FieldServiceProvider"

Publishing should give you a package config file (CKEditor build/toolbar options, upload disk and path, and the media/link/snippet browser settings) plus compiled assets under public/vendor/. Run php artisan storage:link if you plan to upload media to the public disk.

Where to look first, in this order:

  1. README.md — the only documentation; screenshot-driven, so read the code for exact method names
  2. src/CkEditor.php — the field class; every macro/method you can chain on CkEditor::make() lives here or in a trait
  3. config/ in the package — the real source of truth for browser and upload behavior
  4. resources/js/field.js and the Vue components — where the media/link/snippet browser modals and CKEditor instance are wired
  5. dist/ — prebuilt assets; you will not rebuild these unless you fork

Your first use case — a long-form article body with inline images:

use Bayareawebpro\NovaFieldCkEditor\CkEditor;

public function fields(Request $request)
{
    return [
        CkEditor::make('Content')
            ->rules('required', 'string')
            ->hideFromIndex()
            ->help('Rich text; images are uploaded to the public disk.'),
    ];
}

Store it in a longText column, and render it on the front end with {!! $post->content !!} only after sanitizing (see Gotchas). Add ->hideFromIndex() immediately — dumping raw HTML into the index table is a bad first impression.

Implementation Patterns

Field-level configuration stays in the resource. Keep toolbars/options close to the field so reviewers can see what editors can do:

CkEditor::make('Body')
    ->rules('required', 'string', 'max:65535')
    ->options([
        'toolbar' => ['heading', '|', 'bold', 'italic', 'link', 'bulletedList', 'imageUpload'],
    ])
    ->hideFromIndex()
    ->onlyOnForms();

Verify the exact chainable names (options, mediaBrowser, linkBrowser, snippetBrowser, withFiles) against src/CkEditor.php. Treat the resource as the contract; if a method is missing, add it via a small custom subclass instead of scattering config through AppServiceProvider.

Centralize shared editor config. If five resources use the same toolbar and upload disk, wrap it:

class RichText extends CkEditor
{
    public function __construct(string $name, ?string $attribute = null, ?string $resolveCallback = null)
    {
        parent::__construct($name, $attribute, $resolveCallback);

        $this->options(config('ckeditor.toolbar'))
             ->hideFromIndex()
             ->rules('nullable', 'string');
    }
}

Then RichText::make('Body') everywhere. This is the single biggest maintainability win with an unmaintained package — you get one place to patch when you eventually migrate off it.

Uploads: bind the browser to a real disk and path. Configure the upload disk/path in the published config, and keep it consistent with what the front end can serve. A pragmatic layout:

storage/app/public/articles/{id}/...

If uploads are tied to a model, do the disk/path resolution in an observer or a dedicated MediaStorage service rather than in the field, and delete orphaned files in the model's deleting event. Otherwise you accumulate unreferenced files forever, because CKEditor only knows what's in the HTML.

Media/library browser. The browser is a modal that lists assets — in practice you'll point it at a curated directory, an uploads table, or a Spatie Media Library collection. Whatever you pick, make the browser's source and the field's upload target the same backing store. A common failure mode is "uploads go to disk, browser reads a DB table," and then new images never appear in the picker until a sync job runs.

Snippets. Two viable strategies:

  • HTML snippets — the browser inserts markup directly. Simple, but editors can then hand-edit and mangle it, and you lose any ability to change all instances later.
  • Placeholder snippets — insert a token ([[cta-primary]]) and resolve it at render time via a Blade partial map. More work, but the snippet definitions stay version-controllable in code, and output stays consistent.

Choose placeholders if the snippet has brand/legal significance; choose HTML if it's purely decorative.

Rendering and sanitization. The field only produces HTML; the pipeline after it is yours:

use HTMLPurifier;

public function getRenderedContentAttribute(): string
{
    return app(HTMLPurifier::class)->purify($this->content ?? '');
}

Cache the sanitized output (a rendered_content column or a cache key) — purifying large documents on every request is noticeable. On the index view, never show raw HTML:

Text::make('Body', fn ($post) => Str::limit(strip_tags($post->content), 80))
    ->onlyOnIndex(),

Integration tips.

  • Migration: $table->longText('content')->nullable(); — CKEditor documents get big fast; MySQL TEXT (64KB) will truncate
  • Policy/validation: add a max byte length rule so a paste-heavy document can't blow past the column
  • Front-end styling: the editor's output classes (e.g. ck-content) don't ship with your app CSS — either import CKEditor's content styles or write your own typographic CSS for the rendered HTML
  • Seeding/factories: rich text in seeders is painful; keep fixtures as small HTML strings, not screenshots of documents

Gotchas and Tips

Archived status is the headline gotcha. Last release February 2022, 15 stars, no maintainer. There will be no security patches, no Nova 4 support, and no fixes for CKEditor 5 breaking changes. Concretely:

  • Pin the exact version in composer.json ("bayareawebpro/nova-field-ckeditor": "1.x.x"), never ^
  • If you must keep it, fork it under your org and vendor it via a VCS repositories entry — you will need to touch it eventually
  • Write down the migration target now (Nova's native field, a maintained Tiptap/Editor.js Nova field, or a custom form component); budget it as tech debt rather than discovering it mid-upgrade
  • Do not build new features that assume the package will keep pace with Nova

Nova/Vue version mismatch. The field is a Vue 2 component compiled against Nova 3's asset pipeline. Installing it in a Nova 4 app typically produces a blank field or a console error like a failed component resolution — it won't fail at composer install, so test the actual form page before merging.

Asset publishing is a two-step trap. vendor:publish copies prebuilt assets to public/vendor/.... If you edit anything in resources/js, you must rebuild (npm run dev/prod inside the package) and re-publish — and the build toolchain (Laravel Mix, Node version) is from 2022. If assets look stale: php artisan nova:publish, php artisan view:clear, hard-refresh with cache disabled.

CKEditor 5 licensing. CKEditor 5 is GPL2+ or commercial. If your product is closed-source, you need a commercial license key/or you're relying on the GPL build. This is a legal question, not a technical one — flag it before shipping, it's easy to miss when the field "just works."

Silent config failures. If the media/link/snippet browser opens empty rather than erroring, the usual cause is a config key pointing at a nonexistent disk, directory, or route. Check storage/logs/laravel.log and the network tab for the browser's XHR; the modals often swallow 404/403s and just render nothing.

Upload 403s / broken images. In order: is storage:link run, is the disk public (not local), is the file visibility set to public, and — on S3 — is CORS configured for your Nova domain? For signed URLs, CKEditor will happily cache an expired URL in the document HTML; store the storage path, not the URL, and resolve at render time.

XSS is real. CKEditor sanitizes some input, but stored HTML from a compromised editor account (or a hand-crafted API request bypassing Nova) is executable. Always sanitize server-side on save or render. Never {!! !!} raw $request->input() from the same attribute.

Toolbar/option errors surface only in the browser console. An invalid toolbar item name or an option belonging to a CKEditor plugin not in the build produces a JS exception and a dead editor. Open devtools first; the field gives you no PHP-side validation.

displayUsing and other Nova field decorators do nothing here — the field renders a Vue component, not a formatted string. Use ->hideFromIndex() plus a separate Text field for summaries.

Extension points worth knowing. The field class is a normal Nova Field, so subclassing (as shown above) is the cleanest extension point. The Vue component is the place for toolbar/browser behavior changes, but that means maintaining a fork's build. Prefer config + subclassing; avoid patching dist/ by hand, since it's overwritten on the next publish.

Debugging checklist when the field renders blank: confirm the package's service provider is auto-discovered (php artisan package:discover), confirm assets exist in public/vendor/, confirm the resource's field isn't ->onlyOnDetail(), check the browser console for the CKEditor build error, then check storage/logs for upload errors. Nine times out of ten it's unpublished assets or a Nova version mismatch.

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.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib