Summary
JSON-first rich text editor built on FilamentRichEditor / TipTap — premium shell, Gravity icons, content limits with live feedback, responsive image pipeline, optional Spatie Media Library, YouTube embeds, block images, editor image overlay, fullscreen, autosave, paste cleanup, and keyboard-accessible toolbar.
Scope of this doc: only package-specific APIs (variant(),imageVariants(),youtube(), toolbar role presets,FlexRichContentRenderer, etc.). Everything else comes from FilamentRichEditor— see Filament Rich Editor.
| Class | Bjanczak\FilamentFlexFields\Filament\Forms\Components\FlexRichEditor |
| Extends | Filament\Forms\Components\RichEditor |
| State type | array|null (TipTap JSON document) |
| Renderer | Bjanczak\FilamentFlexFields\Filament\Forms\Components\RichEditor\FlexRichContentRenderer |
| FieldType | flex_rich_editor |
Quick start
makeFlexRichContentRenderer() (see Rendering HTML).
Configuration catalog (package API)
FlexRichEditor-specific options only. The field extends FilamentRichEditor; inherited Filament APIs are not duplicated here.
Shell & field chrome
| Method | Type / values | Default | Example section |
|---|---|---|---|
variant() | primary, secondary, soft, flat | secondary | Variants |
size() | sm, md, lg | md | Sizes |
focusOutline() | bool | false | Focus outline |
wordCount() | bool | false | Word count |
jsonBadge() | bool | false | JSON badge |
Content limits & footer
| Method | Type / values | Default | Example section |
|---|---|---|---|
minCharacters() | int | — | Limits |
maxCharacters() | int | — | Limits |
maxWords() | int | — | Limits |
limitBehavior() | soft, hard | soft | Limit behavior |
readingTime() | bool, optional WPM | false, 200 wpm | Reading time |
altTextRequired() | bool | false | Alt text |
Editor UX
| Method | Type / values | Default | Example section |
|---|---|---|---|
fullscreen() | bool | false | Fullscreen |
distractionFree() | bool | false | Distraction-free |
autosave() | int seconds, optional key | off | Autosave |
pasteCleanup() | false, true/standard, aggressive | off | Paste cleanup |
youtube() | bool | false | YouTube |
youtubeNocookie() | bool | true when YouTube on | YouTube |
youtubeWidth() | int px | 640 | YouTube |
youtubeHeight() | int px | 480 | YouTube |
Toolbar
| Method | Type / values | Default | Example section |
|---|---|---|---|
toolbarForRole() | author, editor, admin | — | Toolbar roles |
toolbarForAuthor() / Editor() / Admin() | shortcuts | — | Toolbar roles |
disabledTools() | list<string> | [] | Disabled tools |
getFlexDefaultToolbarButtons(), getFlexFullToolbarButtons(), getFlexDefaultFloatingToolbars(), getFlexExtraTools(), getNativeComparisonToolbarButtons().
To override button layout, use Filament’s toolbarButtons() / floatingToolbars() / tools() with these presets — see Filament Rich Editor.
File attachments & images (package layer)
Requires FilamentfileAttachments() / disk options to be enabled first. This table lists Flex processing and cleanup on top of that baseline.
| Method | Type / values | Default | Example section |
|---|---|---|---|
scopedAttachmentDirectory() | string prefix | — | Scoped directories |
imagesOnly() | — | — | MIME preset |
maxAttachmentSizeKb() | int KB | Filament default | Max upload size |
optimizeImages() | bool | false | Single-pass optimization |
optimizeImagesToWebp() | bool | false | Single-pass optimization |
maxImageWidth() | int px | — | Single-pass optimization |
maxImageHeight() | int px | — | Single-pass optimization |
maxImageLongEdge() | int px | — | Single-pass optimization |
stripExif() | bool | true | Single-pass optimization |
imageVariants() | array / RichEditorImageVariant | [] | Named variants |
pruneOrphanedAttachmentsOnSave() | bool | true | Orphan cleanup |
HTML rendering
| Method | Type / values | Default | Example section |
|---|---|---|---|
responsiveImages() | bool | auto when variants set | Responsive HTML |
lazyImages() | bool | true | Lazy loading |
imageSizes() | CSS sizes string | 100vw | Image sizes |
makeFlexRichContentRenderer() | mixed JSON | — | Rendering HTML |
youtube()), paste cleanup (when pasteCleanup()).
Shell variants
primary
secondary (default)
soft
flat
Shell sizes
sm
md (default)
lg
Focus outline
Word count
JSON debug badge
Content limits
Soft limits (default) — warn in footer, validate on save
Hard limits — block typing past max
editor.commands.undo() when the user exceeds max characters or words.
Min characters only
Max characters only
Max words only
Reading time
config('filament-flex-fields.rich_editor.reading_time_words_per_minute').
Fullscreen
Distraction-free
Hides clearFormatting and clearContent in the toolbar while fullscreen is active.Autosave
Saves editor JSON tolocalStorage. On reload, prompts to restore if a newer draft exists.
{statePath}::{model:id|create}::user:{id}.
Paste cleanup
| Call | Mode | Behavior |
|---|---|---|
| (none) | off | No paste plugin |
pasteCleanup() or pasteCleanup(true) | standard | Strip inline style/class, Office markup |
pasteCleanup('aggressive') | aggressive | Standard + unwrap <span> / <font> |
YouTube embeds
Requires->youtube(). Adds toolbar button + TipTap extension + paste handler for YouTube URLs.
youtube.com/watch?v=, youtu.be/, youtube.com/embed/, youtube.com/shorts/.
Rendered HTML preserves data-youtube-video iframes through FlexRichContentRenderer::toHtml() sanitization.
Block images (automatic)
FlexRichEditorBlockImagePlugin is always registered. Images inserted in the editor are block-level (not inline with paragraph text). No configuration required.
Image overlay (editor only)
When file attachments are enabled, selecting an image shows Edit and Delete controls (top-right overlay). Not included in frontend HTML output.- Edit → opens Filament
attachFilesaction (alt text, replace). - Delete → removes image from document.
Toolbar presets
Default Flex toolbar (no attachFiles)
Full toolbar (with attachments button)
Custom toolbar
Native Filament comparison (playground)
Toolbar roles
Presets inconfig('filament-flex-fields.rich_editor.toolbar_roles').
Author
Editor
Admin
Custom role (after publishing config)
Disabled toolbar tools
flexFullscreen and plugin tools (youtube) use their tool names.
Floating toolbars
Default paragraph bubble (bold, italic, underline, strike, link). Hidden when an image is selected.Custom tools
Flex adds Gravity-icon tools viaFlexRichEditor::getFlexExtraTools():
clearFormatting— clear nodes + marksclearContent— empty document
fullscreen() is enabled, flexFullscreen tool is appended automatically.
File attachments
Uploads use Filament’s attachment API (fileAttachments(), disk, directory, visibility). Flex adds scoped paths, optimization, variants, and orphan cleanup on top.
Attachments are enabled when fileAttachments(true) or the toolbar includes attachFiles and Filament resolves hasFileAttachments().
Visibility
Scoped attachment directories
scopedAttachmentDirectory('rich-editor') resolves:
{prefix}/{model}/{id}/when editing a record{prefix}/drafts/{userId}/on create forms
Images only
Restricts accepted MIME types to images (JPEG, PNG, GIF, WebP).Max attachment size
Single-pass image optimization
Without named variants — resizes/optimizes the master file on upload.Named image variants
Generatesphoto__thumb.webp, photo__large.webp, and photo.jpg.flex-variants.json.
Array config
Fluent RichEditorImageVariant objects
max_long_edge, max_width, max_height, webp, master, optimize.
Automatic attachment cleanup
Native disk (default on)
.flex-variants.json manifests. Scoped directories also sweep unreferenced files.
Spatie Media Library
Disk pruner is skipped. Filament callsFileAttachmentProvider::cleanUpFileAttachments(exceptIds: [...]) on save.
Responsive images in HTML output
Lazy images on/off
Responsive srcset on/off
responsiveImages() defaults to on if not set explicitly.
Image sizes attribute
Rendering HTML
Always usemakeFlexRichContentRenderer() so disk, variants, plugins (YouTube), and lazy loading match the field.
From field instance (recommended)
In Filament infolist / table column
Standalone renderer
Example output
Blade view
Inertia / API controller
Alt text required
Accessibility
Built-in without extra configuration:| Feature | Behavior |
|---|---|
| Toolbar | role="toolbar", aria-orientation="horizontal", aria-label from field label |
| Roving tabindex | Arrow keys, Home, End between toolbar buttons |
| Editor | role="textbox", aria-multiline="true" |
| Footer stats | role="status", aria-live="polite" |
| Autosave | aria-busy while saving |
| Limit warnings | aria-label reflects warning/danger state |
Spatie Media Library (optional)
1. Model — register conversions
2. Form
3. Render
| Native disk | Spatie | |
|---|---|---|
data-id | File path | Media UUID |
| Variants | photo__thumb.webp + manifest | Spatie conversions |
| Orphan cleanup | RichEditorAttachmentPruner | cleanUpFileAttachments() |
Complete example: Filament Resource
Configuration recipes
Minimal blog post
Production article (native disk)
Author role (limited toolbar)
Package config
config/filament-flex-fields.php:
Translations
Package strings:filament-flex-fields::default.rich_editor.* in resources/lang/en/default.php and resources/lang/pl/default.php.
Toolbar labels reuse Filament: filament-forms::components.rich_editor.tools.*.
Publish translations
Key reference
| Key | English | Polski |
|---|---|---|
rich_editor.clear_content | Clear content | Wyczyść treść |
rich_editor.youtube.tool | YouTube | YouTube |
rich_editor.word_count.line | :characters characters, :words words | :characters znaków, :words słów |
rich_editor.reading_time.line | :minutes min read | :minutes min czytania |
rich_editor.limits.max_characters | Content may not exceed :max characters. | Treść nie może przekraczać :max znaków. |
rich_editor.autosave.saved | Draft saved | Szkic zapisany |
rich_editor.alt_text.validation | :count image(s) are missing alt text. | :count obraz(ów) nie ma tekstu alternatywnego. |
rich_editor.fullscreen.toggle | Toggle fullscreen | Przełącz pełny ekran |
rich_editor.image.edit | Edit image | Edytuj obraz |
rich_editor.image.delete | Delete image | Usuń obraz |
Testing
PHP (package)
JavaScript unit tests
Playwright E2E (playground)
Playground
EnableFLEX_FIELDS_PLAYGROUND=true and open Flex Rich Editor (/flex-fields-playground/flex-rich-editor).
Includes:
- Native Filament
RichEditorcomparison - Full Article body demo (attachments, variants, limits, YouTube, fullscreen, autosave)
- Variant grid: compact / secondary / soft / flat
- Attachments-only and full-toolbar sections
Assets (CSS & JS)
Follows the lazy-asset pattern fromDEVELOPMENT.md (§3–§5 in the package repo): one lazy CSS bundle per field, Alpine x-load per component, optional support scripts loadedOnRequest().
| Asset | Kind | When it loads |
|---|---|---|
rich-editor-field.css | Lazy CSS | load-stylesheet in blade when the field renders |
flex-rich-editor.js | Alpine component | x-load on the editor root |
flex-rich-editor-paste-extension.js | TipTap extension | When pasteCleanup() is enabled |
flex-rich-editor-block-image-extension.js | TipTap extension | Always (block images) |
flex-rich-editor-youtube-extension.js | TipTap extension | When youtube() is enabled |
Does it duplicate Filament’s rich editor?
On a page with onlyFlexRichEditor: no double download of Filament’s rich-editor Alpine asset. The blade loads flex-rich-editor, not filament/forms → rich-editor.
JS bundle: at build time, flex-rich-editor.js bundles Filament’s rich-editor-form-component (esbuild alias to vendor/filament/forms/dist/components/rich-editor.js) plus Flex chrome (~500 KB raw — same order of magnitude as native Filament). That is a single runtime script, not Filament + Flex side by side.
CSS: Filament does not ship a separate rich-editor.css asset. Flex adds rich-editor-field.css for the fff-rich-editor-* shell and overrides on shared fi-fo-rich-editor-* markup classes. No second full editor stylesheet.
Exception — playground comparison: the Flex Rich Editor playground page may render native RichEditor next to FlexRichEditor for demo purposes. That page loads both rich-editor and flex-rich-editor scripts — intentional, not typical production usage.
Performance notes
- Footer stats use
requestAnimationFramebatching (not per-keystroke DOM thrashing). - Image overlay sync runs on selection changes only.
- Attachment manifest reads are cached per request during HTML rendering.
- Bundle budget (CI):
flex-rich-editor.js~497 KB raw / ~157 KB gzip (aligned with Filament native).
Requirements & optional packages
| Package | Purpose |
|---|---|
filament/forms ^5 | Base RichEditor / TipTap |
spatie/laravel-medialibrary | Optional — Spatie attachment provider + conversions |
filament/spatie-laravel-media-library-plugin | Optional — Filament Spatie file attachment provider |
| GD extension | Recommended for on-disk variant generation |
Filament RichEditor baseline
FlexRichEditor extends Filament\Forms\Components\RichEditor. Options not documented on this page behave as in Filament.
- Filament Rich Editor documentation
- JSON storage is enabled by default in
setUp()(json())