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

apimediaru/nova-tinymce

A Laravel Nova field that integrates the TinyMCE WYSIWYG editor into your admin panel, giving users a familiar rich-text editing experience with configurable toolbars and options for managing formatted content.

View on GitHub
Deep Wiki
Context7

Getting Started

1. Install

composer require <vendor>/nova-editor
php artisan nova:publish   # publish config + frontend assets

2. Publish the assets (2.0.4+)

Add the laravel-assets tag to your Nova layout / app.blade.php so the package's compiled JS and CSS are injected and auto-published for you instead of copying files by hand:

@laravelAssets

If you are on an older Nova version or a custom layout, keep the manual php artisan vendor:publish --tag=editor-assets step.

3. Look here first

  • config/nova-editor.php — tools, upload disk/path, sanitization toggles.
  • vendor/<vendor>/nova-editor/resources/js — the Vue side, if you need custom tools or blocks.
  • The field class (Editor) — this is what you reference in your Resource.

4. First use case

Replace a plain textarea with the editor for a typical “Article body” field:

use Vendor\NovaEditor\Editor;

public function fields(NovaRequest $request)
{
    return [
        Text::make('Title')->rules('required', 'max:255'),
        Editor::make('Body')
            ->rules('required')
            ->help('Rich text; stored as JSON.'),
    ];
}

Your body column should be json/longText holding the editor’s block document. Keep a rendered HTML copy (or render on demand) so your frontend never has to parse the block format.

Implementation Patterns

Store blocks, render HTML separately. Treat the editor’s output as source-of-truth JSON and render it for the frontend:

// app/Models/Article.php
protected $casts = ['body' => 'array'];

public function bodyHtml(): string
{
    return app(\Vendor\NovaEditor\Renderer::class)->render($this->body ?? []);
}

Cache the rendered HTML (body_html) on save if the field is read on high-traffic pages.

Reusable field presets. Define named configs (minimal, full, comment) and pass the profile from the Resource rather than repeating options:

Editor::make('body')->withProfile('full')->hideFromIndex(),
Editor::make('notes')->withProfile('minimal')->alwaysShow(),

Uploads / media. Point the package at a dedicated disk so image URLs, deletion, and pruning stay predictable:

// config/nova-editor.php
'uploads' => [
    'disk' => env('EDITOR_DISK', 'public'),
    'path' => 'editor/{user_id}/{date:Y}/{date:m}',
    'max_size' => 5120,
],

Serve uploads through a controller if the disk is private, and mirror the same disk/path in any frontend renderer so relative URLs resolve.

Nested components (fixed in 2.0.4). Editors inside Nova’s nested resources, Panel, Tab, and relationship HasMany sub-forms now mount correctly. Pattern:

HasMany::make('Sections'),

// Section resource
Editor::make('body')->rules('required'),

When a parent form re-creates child rows, give each row a stable key (->resolveUsing(...)/unique attribute) so the editor keeps its content instead of reinitialising empty.

Custom tools/blocks. Register your JS tool against the editor instance and expose it via the package’s config; keep block renderers in one place so the Nova preview and the site output do not drift. Build with the package’s own toolchain, not your app’s Vite pipeline.

Validation & sanitization. Rules on the field only see the JSON blob, so add a custom rule that walks blocks (required, max blocks, allowed block types) and sanitize HTML server-side before persisting body_html.

Programmatic updates. When saving via API/imports, validate the document shape before assigning — a malformed array will render an empty editor and look like data loss.

Gotchas and Tips

  • Asset caching after upgrade. Stale JS after composer update is almost always unpublished/browser-cached assets. Run php artisan nova:publish, php artisan view:clear, hard-refresh. On 2.0.4, verify @laravelAssets is present in your layout — without it the new auto-publishing path never runs and editors silently fail to boot.
  • Nested editors mounting twice. Tabs/panels that re-render on toggle can re-mount the field. Keep state in the model/JSON value, not in component-local refs, and avoid onMounted handlers that assume a single mount.
  • Empty output looks identical to broken output. A block document with an unexpected shape renders empty. Log/validate on save; add a Nova index preview that shows a text extraction so you catch blank bodies.
  • Sanitize on write, not on read. Whitelist tags/attributes server-side; do not trust the client bundle. Store both JSON and sanitized HTML to avoid sanitizing on every request.
  • Config cache. config/nova-editor.php changes need php artisan config:clear in local; in production the cached config wins over .env edits.
  • Upload disk mismatch. The editor’s stored URL is generated at upload time. Changing disk or path later orphans existing images — migrate references, don’t just flip config.
  • Deleting a model doesn’t delete its uploads. Add a model deleting hook or scheduled prune for the upload path, otherwise storage grows quietly.
  • Do not share a Vite build. Compiling this package’s JS into your app bundle typically yields duplicate Vue instances and broken toolbars. Keep the Nova-published assets separate.
  • Extension points to remember: field config profiles, the block renderer (override via container binding), the upload handler, and the frontend tool registry. Prefer these over editing vendor/ files, which you will lose on the next release.
  • Upgrade path. 2.0.4 is additive (nested editor fix + laravel-assets publishing); drop the manual publish scripts from your deploy pipeline once @laravelAssets is wired in, and re-test any editor inside HasMany/Panel forms since the mount lifecycle changed.
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.
mydnic/volet-feature-board-filament-plugin
wmde/email-address
refinery29/test-util
dees040/laravel-api-responses
teamzac/geocoder
ideatica/config-writer
keaton-php/pipe-dream
jacksonwebservices/laravel-user-suite
maksimru/conditional-laravel-debugbar
gismatul/nova-support-chat
gismatul/laravel-support-chat
networkrailbusinesssystems/oracle-api
alphabalex/messaging-made-easy
rawphp/warp
shootkiran/merobug
narakode/fineauth
numencode/wn-syncops-plugin
ibnnajjaar/mib-global-pay
refinery29/php-cs-fixer-config
parsnick/steak