Customizing the Rich Text Editor Toolbar

Introduction

Every rich_text_editor form field — Shop product/category descriptions, blog comment bodies, or a custom module's own HTML field — renders through one shared, zero-dependency component: Zero\Support\Forms\RichTextEditorField, backed by a single contenteditable template at src/Modules/Admin/Views/editor.php. Every instance used to get the exact same fixed toolbar (bold, italic, underline, lists, link, table, clear-formatting), whether the field needed all of that or not.

Opting a Field Into a Reduced Toolbar

The editor template now resolves its button set from a small $toolbarSets registry, keyed by a toolbar value read straight out of the field's own config array — so any model's getConfig() can opt in with one extra key, no new field type required:

'description' => [
    'type' => 'rich_text_editor',
    'toolbar' => 'basic',
    'label' => 'Description',
    'editable' => true,
],

Omitting toolbar entirely keeps the original full button set, so every pre-existing rich_text_editor field elsewhere in the codebase is unaffected.

The Toolbar Registry (editor.php)

The Toolbar Registry (editor.php) php
$buttons = [
    'bold'                => '<button type="button" data-cmd="bold"><strong>B</strong></button>',
    'italic'              => '<button type="button" data-cmd="italic"><em>I</em></button>',
    'underline'           => '<button type="button" data-cmd="underline"><u>U</u></button>',
    'insertUnorderedList' => '<button type="button" data-cmd="insertUnorderedList">UL</button>',
    'insertOrderedList'   => '<button type="button" data-cmd="insertOrderedList">OL</button>',
    'createLink'          => '<button type="button" data-cmd="createLink">A</button>',
    'insertTable'         => '<button type="button" data-cmd="insertTable" title="Insert Table">Table</button>',
    'removeFormat'        => '<button type="button" data-cmd="removeFormat">Clear</button>',
];

$toolbarSets = [
    'full'  =&gt; array_keys($buttons),
    'basic' =&gt; ['bold', 'italic', 'underline', 'insertUnorderedList', 'insertOrderedList', 'createLink', 'removeFormat'],
];

$activeButtons = $toolbarSets[$toolbar ?? 'full'] ?? $toolbarSets['full'];

Adding a New Preset

A new preset is just another array key naming a subset of $buttons — e.g. a 'minimal' preset with only ['bold', 'italic', 'createLink']. No JavaScript changes are ever required: public/assets/js/admin.js's toolbar handler is fully generic — it delegates off whichever [data-cmd] buttons actually exist in the rendered DOM rather than hardcoding a fixed button list, so it automatically adapts to whatever editor.php ends up printing.

The Toolbar Is UX, Not a Security Boundary

Trimming the toolbar only changes which buttons an editor sees — it does nothing to stop a pasted <table>, a browser extension, or a direct API write from landing block-level HTML in a field's stored value regardless of preset. The rendering side must independently enforce whatever tag set it actually wants to allow, with its own allow-list sanitizer — matching the toolbar's button set is a courtesy to the editor, not a substitute for sanitizing on render.