alfonsobries/nova-trumbowyg
A Laravel Nova field that integrates the lightweight Trumbowyg WYSIWYG editor, giving your Nova admin resources a clean, customizable rich-text input for creating and editing HTML content.
Requirements first: this field was last released in 2018 and the repo is archived. Check composer.json before installing — it targets Nova 1.x (and therefore Vue 2 + the older Nova asset pipeline). It is not a drop-in for Nova 2/3/4.
composer require alfonsobries/nova-trumbowyg
If the package ships published assets, run php artisan vendor:publish once; otherwise the compiled JS/CSS in dist/ is loaded by the field component itself.
First use case — replacing a Textarea on a Post/Blog resource:
use Alfonsobries\NovaTrumbowyg\NovaTrumbowyg;
public function fields(Request $request)
{
return [
ID::make()->sortable(),
Text::make('Title')->rules('required', 'max:255'),
NovaTrumbowyg::make('Body')
->rules('required')
->help('Rich text is stored as raw HTML.'),
];
}
The field resolves to an HTML string, so the column should be text/longText. Render it with {!! $post->body !!} (after sanitizing) — the editor is only the input mechanism.
Where to look first (in this order):
src/NovaTrumbowyg.php — the field class and its options() / withOptions() API. Confirms the exact method names used below.resources/js/components/Field.vue — how Trumbowyg is mounted on the wrapper element, and where the initialization options are read from.resources/js/field.js / dist/js/* — how the jQuery/Trumbowyg assets are registered with Nova.alex-d.github.io/Trumbowyg) for the actual option names (btns, btnsDef, semantic, resetCss, lang, plugins).Configuring the toolbar — pass Trumbowyg options straight through (JSON-serialized into the component):
NovaTrumbowyg::make('Body')
->options([
'lang' => 'en',
'resetCss' => true,
'btns' => [
['viewHTML'],
['undo', 'redo'],
['formatting'],
['bold', 'italic', 'underline', 'strikethrough'],
['link'],
['justifyLeft', 'justifyCenter', 'justifyRight', 'justifyFull'],
['unorderedList', 'orderedList'],
['removeformat'],
],
'semantic' => ['div' => 'p'],
]);
Keep the toolbar small — Trumbowyg's differentiator is a lightweight editor, and a 30-button toolbar in a Nova form is worse than a Markdown textarea.
Splitting display from editing. The raw HTML string is a poor index/detail cell. Standard pattern:
NovaTrumbowyg::make('Body')->onlyOnForms(),
Text::make('Body', 'body')
->onlyOnIndex()
->displayUsing(fn ($value) => Str::limit(strip_tags($value), 80)),
Text::make('Body')
->onlyOnDetail()
->asHtml(), // or displayUsing() returning an HtmlString of sanitized HTML
Sanitization / normalization. Never trust the submitted HTML. Either sanitize in the Nova field's fill()/resolveUsing via an observer/mutator, or use a Spatie\Html-style sanitizer:
public function setBodyAttribute($value)
{
$this->attributes['body'] = app(Sanitizer::class)->sanitize($value);
}
Same field, multiple times on one form. Trumbowyg attaches to a DOM node, not a global. Confirm the Vue component scopes its selector to its own $el; if you see two editors fighting over one instance, that's why. Give each field a distinct attribute and test side-by-side.
Searchable / sortable. The underlying column is text, so ->searchable() works on the raw text (HTML tags included in the query) — acceptable for most uses, misleading if you rely on it heavily. Consider a separate plain-text column populated in the mutator if search matters.
Reusing rendered output. Treat the resource field purely as input; render the stored HTML through a Blade partial (resources/views/posts/body.blade.php) so front-end typography styles live in your app, not in Nova.
src/ against your installed Nova version — the Vue 2 component API and Nova::script() asset registration are the usual breakages. If you're on Nova 3+, look at actively maintained alternatives (nova-tiptap, CKEditor or TipTap-backed fields) or build a thin custom field around Trumbowyg yourself.beforeDestroy, navigating between resources repeatedly leaks DOM listeners. Add trumbowyg.destroy() in a $destroy hook as an extension point..trumbowyg-modal z-index is the standard fix; scope it in your Nova CSS (Nova::style()) rather than editing dist/.btns (array of button groups) and btnsDef (button definitions) — not a flat buttons list. Passing the wrong key fails silently: the editor renders with the default toolbar. Verify by inspecting the mounted component's props in Vue devtools.File field to handle uploads — you'd need a custom route + plugins config, at which point a maintained editor is usually cheaper.resetCss. With resetCss => true the editor strips browser heading/paragraph styling; without it, content pasted from Word looks wildly different between the editor and your front end. Test paste behavior early.langs/; if lang is set but you see English, the asset path isn't published.<p><br></p> for an "empty" editor. Use a nullable/required rule combined with a custom trim-style check (strip_tags($value) === '') if the field is mandatory.trumbowyg() is called, that jQuery is loaded, and that Trumbowyg's CSS is present — a missing stylesheet makes the editor look like an unstyled textarea rather than throwing.How can I help you explore Laravel packages today?