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

Tinymce Field Laravel Package

backpack/tinymce-field

Backpack TinyMCE Field adds a ready-to-use TinyMCE WYSIWYG editor to Laravel Backpack admin forms, making it easy to create, configure, and manage rich text fields with a familiar, powerful editing experience.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the field type alongside Backpack:

composer require backpack/tinymce-field

Backpack auto-discovers the field's service provider (field packages register themselves via Backpack\CRUD\app\Library\CrudPanel field loading), so there's no manual config/app.php entry in modern versions. If nothing renders, run php artisan vendor:publish --provider="Backpack\TinyMCEField\TinyMCEFieldServiceProvider" (or check the package README for the exact provider name) and php artisan config:clear.

Where to look first

  1. vendor/backpack/tinymce-field/resources/views/ — the Blade file that renders the <textarea> and pushes the init script. This is the ground truth for which options are supported and which asset stacks are used.
  2. vendor/backpack/tinymce-field/README.md — the canonical option list (options, height, toolbar config, CDN vs. self-hosted URLs).
  3. Your app's config/backpack/ directory for the CRUD defaults, since the field inherits a lot of behavior from Backpack core.

First use case — swap a plain textarea for a rich editor on your main content field:

// Backpack 5/6 fluent style
CRUD::field('content')->type('tinymce')->size(12);

// Backpack 4.2 array style
$this->crud->addField([
    'name'  => 'content',
    'type'  => 'tinymce',
    'label' => 'Body',
]);

That's the whole happy path. The default setup loads TinyMCE from the CDN, gives you the standard toolbar, and syncs the editor back into the hidden <textarea> on form submit.

Implementation Patterns

Per-field TinyMCE init config. The field accepts an options array that is JSON-encoded and passed straight to tinymce.init(). This is where you do almost all customization:

CRUD::field('content')->type('tinymce')->options([
    'menubar'      => false,
    'height'       => 500,
    'plugins'      => 'lists link table code fullscreen',
    'toolbar'      => 'undo redo | bold italic | bullist numlist | link table | code',
    'branding'     => false,
]);

Two flavors of the same field. Most projects end up with a "full" editor for long-form content and a "compact" one for short fields (product blurbs, meta descriptions). Define them once so you don't repeat a 15-line options array on every CRUD:

// app/Support/Fields.php
class Fields
{
    public static function richText(string $name, array $overrides = [])
    {
        return CRUD::field($name)->type('tinymce')->options(array_merge([
            'menubar' => false,
            'height'  => 450,
            'plugins' => 'lists link table code fullscreen',
        ], $overrides));
    }

    public static function miniText(string $name)
    {
        return self::richText($name, [
            'height'  => 150,
            'toolbar' => 'bold italic | link',
        ]);
    }
}

That gives you one place to bump the toolbar or add a plugin across the whole admin panel.

Self-hosting TinyMCE. CDN loading is fine for most admin panels but is a hard no for air-gapped deployments, CSP-strict environments, or EU data-residency setups. Pull the TinyMCE npm package into public/vendor/tinymce, then point the field at it via the URL option (check the README for the exact key — it's typically tinymce_url or passed inside options). Doing this also removes a third-party request from every admin page load, which is a nice latency win.

File/image uploads. TinyMCE's image button inserts a base64 blob by default, which bloats your content column and breaks once records get large. The two practical patterns:

  1. Keep images out of the editor entirely — add a separate Backpack image/upload_multiple field on the model and let the editor handle only prose plus links.
  2. Register a custom images_upload_handler inside options that POSTs to a Backpack route and returns the stored URL. This requires a small custom controller endpoint with auth + validation — don't skip the validation.

Server-side sanitization is non-negotiable. TinyMCE is a JavaScript editor; a user can POST anything to the update route with curl. Every model that has a TinyMCE field should purify HTML on save (e.g. mews/purifier) or render it through a strict allowlist. Do this at the model mutator level, not the controller, so seeders, imports, and artisan commands get the same treatment.

Rendering the output. Backpack's show operation and your front end both need {!! $model->content !!} — and that's exactly why the purifier step above matters. If you want a stricter front-end posture, store both the raw HTML and a pre-sanitized "safe" copy, or sanitize again at render time.

Repeaters and nested fields. If you use repeatable or relationship fields that clone form rows via JS, TinyMCE instances inside cloned rows won't initialize on their own. The package handles single fields cleanly; for repeatables you generally need a small custom script that calls tinymce.init() on newly added textareas and tinymce.remove() when rows are deleted.

Gotchas and Tips

AJAX form submits lose the content. TinyMCE mirrors its content into the underlying <textarea> on normal form submission. If your CRUD form submits via fetch/Axios (custom modal, inline save), you must call tinymce.triggerSave() before serializing, or the POST arrives with stale/empty content. This is the single most common bug report pattern.

Multiple editors on one page. Two tinymce fields on the same CRUD form each need a distinct selector, which the package handles — but if you roll your own init script on top, make sure you're not double-initializing or targeting textarea globally. Debug by inspecting the rendered HTML: each textarea should have a unique id, and each tinymce.init() call should target that specific id.

Version pinning against Backpack. Backpack's major versions (4.2 → 5 → 6) changed asset stacks and field-registration APIs. Check the package's composer.json constraints against your Backpack version before upgrading either one. A mismatched pair typically manifests as the field rendering a bare textarea with no JS error, because the script was pushed to an asset stack your layout no longer renders.

Asset stack rendering. The field pushes JS to something like after_scripts. If you've built a custom CRUD layout, Blade view, or dashboard shell that doesn't render that stack, the editor silently won't load. Quick test: view()->getEngineResolver() aside, just grep your layout for @stack('after_scripts') / @stack('scripts').

CDN failures are quiet. If the TinyMCE CDN is blocked (corporate proxy, CSP script-src policy, offline CI), you get an unstyled textarea with a console 404 — not a visible error. If a client reports "the editor disappeared," check the browser console and network tab first, then CSP headers.

Purifiers eat valid editor output. HTMLPurifier's default config strips <iframe> (embedded videos), some data-* attributes, and inline styles — all things TinyMCE happily produces. Configure your allowed elements/attributes deliberately, or you'll get support tickets about disappearing YouTube embeds. Test with a real saved-and-reloaded record, not just the editor preview.

Content length. TEXT (65KB) fills up faster than people expect once you allow inline images or pasted-from-Word content. Use MEDIUMTEXT/LONGTEXT for anything that allows code, table, or paste-from-Word. Also worth adding a strip_tags length guard in your model if you want a rough "word count" for listings.

Listing views. tinymce is a form field, not a column type. In list and show you'll want limit(120) on a text column, or a custom column that runs strip_tags() — otherwise your datatable explodes with raw HTML.

Pasting from Word/Google Docs. Modern TinyMCE handles this reasonably, but it drags in mso- styles and inline font declarations. Either enable powerpaste (paid) or accept that your sanitizer is doing the cleanup pass. Test a paste from Google Docs into a fresh record before you sign off on the field.

Toolbar parity with the front end. The editor shows H2/H3 and bullet lists; if your front-end CSS doesn't style them, the published page looks broken while the admin preview looks fine. Keep a "content stylesheet" loaded into the editor (content_css option) that mirrors your public typography — this is the cheapest way to make "what you see is what you get" actually true.

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