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

Localtinymce Laravel Package

gusmanson/localtinymce

A Laravel package that bundles TinyMCE locally so you can add a rich text editor without relying on a CDN. It publishes the editor assets and offers simple configuration for integrating TinyMCE into Blade views.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the package in a Laravel app that already has Nova installed:

composer require gusmanson/localtinymce

Publish the package assets so TinyMCE's JS/CSS and the upload endpoint config land in your app:

php artisan vendor:publish --provider="Gusmanson\\Localtinymce\\FieldServiceProvider"

Check config/localtinymce.php (if present) and public/vendor/localtinymce/ after publishing — that's where the self-hosted TinyMCE build lives. If your build pipeline uses php artisan storage:link, run it now so uploaded images resolve via the public disk.

First use case — a rich-text field on a Nova resource:

use Gusmanson\Localtinymce\LocalTinyMCE;

public function fields(Request $request)
{
    return [
        ID::make()->sortable(),
        Text::make('Title'),
        LocalTinyMCE::make('Body'), // stored as HTML in a text/longText column
    ];
}

Where to look first:

  • resources/js/field.js or the compiled public/js/field.js — this is the Vue component wrapping the TinyMCE editor instance.
  • The package's service provider — it registers the upload route and asset publishing.
  • config/localtinymce.php — the main knobs for editor options and upload behaviour.

First smoke test: open the resource form, type into the field, drag a PNG into the editor, save. Verify the file landed on your configured disk and the saved HTML contains a working <img src="..."> URL. If the image renders but the URL is a relative path that breaks on a different host, that's the first thing to fix (see Gotchas).

Implementation Patterns

Multiple fields, different editors. TinyMCE is initialised per component instance by the Vue wrapper, so a resource can have several LocalTinyMCE fields. If the wrapper reuses an element ID, scope the editor init to the component's own DOM node — keep that in mind if you fork the field component.

Composing with Nova's field API. Because it extends Nova's Field, all the usual modifiers work:

LocalTinyMCE::make('Content')
    ->rules('required', 'max:65535')
    ->hideFromIndex()          // HTML in a table cell is unreadable
    ->help('Basic formatting only');

LocalTinyMCE::make('Summary')
    ->onlyOnForms()
    ->withMeta(['toolbar' => 'bold italic link']);

Prefer hideFromIndex() on every HTML field — Nova's index view renders raw HTML otherwise.

Sanitising on the way in. TinyMCE output is HTML, so treat it as untrusted. A common pattern is a Nova action or model mutator that runs the payload through an HTML purifier before persisting, or a FormRequest rule. The package does not sanitise for you — it just hands you the editor's HTML.

Upload integration tips.

  • Point uploads at a dedicated disk/folder in the config (e.g. public/uploads/tinymce) rather than the default media library path, so you can purge orphans independently.
  • If you need auth-aware uploads (per-tenant folders, per-user quotas), override the upload controller/route — publish-then-extend is safer than editing vendor files.
  • For S3-backed uploads, confirm the returned URL is absolute (Storage::url() semantics) before it reaches the editor.

Rendering on the front end. Store HTML as-is and render with {!! $model->body !!} only after sanitising. Alternatively, keep the raw HTML and render through a Markdown/HTML pipeline that strips scripts.

Local-first workflow. The main selling point: no CDN calls. This matters for offline dev, air-gapped environments, and GDPR-sensitive setups where third-party scripts are blocked. Verify your public/vendor assets are deployed in CI — some build pipelines skip them.

Gotchas and Tips

Asset publishing is easy to forget in deploys. public/vendor/localtinymce/ is not committed by default and not rebuilt by npm run build. Add php artisan vendor:publish --force --tag=localtinymce-assets (or the equivalent) to your deploy script, or commit the assets.

Editor initialised with the wrong height / hidden in tabs. TinyMCE inside Nova tabs or modals often needs a manual resize because the container has zero height when tinyMCE.init runs. If you fork the Vue field, call tinymce.get(id).mode.set('design') or a resize on the next tick.

Upload route 419/401 errors. The packaged upload endpoint is likely behind Nova's auth middleware. If uploads fail while the rest of Nova works, check:

  • config/localtinymce.php for the route prefix and middleware.
  • The X-CSRF-TOKEN header the Vue component sends (it should read from document.querySelector('meta[name="csrf-token"]')).
  • That your session cookie domain matches the upload URL (S3 or a separate subdomain will break this).

Relative image URLs. Nova runs on one host/path; if your front-end app lives elsewhere, stored <img src="/storage/..."> breaks. Configure a full URL base (env-driven) in the upload response, not the raw storage path.

Toolbar/config overrides don't stick. TinyMCE options passed via withMeta are usually merged into the JS init() call, but if the package hardcodes an init object, overrides may be ignored. Inspect the Vue component to confirm the merge order — later object keys win, so pass overrides last.

No sanitisation, no version guarantee. With zero stars and no dependents, expect an unmaintained dependency. Pin the version in composer.json ("gusmanson/localtinymce": "1.x"), and audit the bundled TinyMCE version — an old TinyMCE build is a real XSS/CVE risk. Consider swapping just the vendored tinymce/ directory for a current self-hosted build if you keep the field component.

Extension points to lean on:

  • Override the upload controller by binding your own class to the route (Laravel's route caching respects route order — register yours after the package's).
  • Add TinyMCE plugins by dropping them into the published vendor folder and listing them in the toolbar config.
  • If the component doesn't expose enough, forking the Vue field into your own Nova custom field is usually faster than patching vendor/ — you'll re-apply patches on every composer update otherwise.

Debugging checklist when the editor "doesn't appear":

  1. Open DevTools Network — did tinymce.min.js load 200 from /vendor/...?
  2. Console — any "tinymce is not defined" means the wrapper's script ran before the asset loaded; check the field component's mounted hook.
  3. Does the textarea get replaced at all, or is it empty? Empty means mounted never fired — usually a Nova version mismatch.
  4. Check nova-components build — Nova compiles field assets on install; php artisan nova:publish may be needed after upgrading Nova.
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.
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
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