ksquaredcoding/filament-tiptap-editor
A Tiptap-powered rich text editor field for Filament forms, bringing a clean WYSIWYG experience to Laravel admin panels with configurable toolbar controls, rich content editing, and straightforward integration into Filament resources.
Requirements: PHP 8.2+, filament/filament ^4.0 or ^5.0, and this package at ^4.1 if you're on Filament 5 (4.0.x will not resolve against it).
composer require ksquaredcoding/filament-tiptap-editor
# config + views/blocks
php artisan vendor:publish --tag="filament-tiptap-editor-config"
php artisan vendor:publish --tag="filament-tiptap-editor-views"
# frontend assets (do this every time the package updates)
php artisan filament:assets
The service provider is auto-discovered by Filament; there is no manual panel registration unless you want to override the editor views.
Where to look first:
config/filament-tiptap-editor.php — this is the whole package. profiles defines the toolbar button sets (default, minimal, all, plus anything you add), and the media disk/directory keys live here.TiptapEditor field class — it's a standard Filament Field, so ->required(), ->columnSpanFull(), ->helperText() and friends all work.First use case — swapping out RichEditor on a Post resource with zero schema changes because both persist an HTML string:
use FilamentTiptapEditor\TiptapEditor;
public static function form(Schema $schema): Schema
{
return $schema->components([
TextInput::make('title')->required(),
TiptapEditor::make('body')
->profile('default')
->columnSpanFull()
->required(),
]);
}
A text/longText column is enough. Nothing to cast, nothing to migrate.
Define profiles once, reuse everywhere. Declare named toolbars in config rather than repeating ->tools([...]) per form:
// config/filament-tiptap-editor.php
'profiles' => [
'default' => [
'toolbarButtons' => ['h2', 'h3', 'bold', 'italic', 'link', 'bulletList', 'orderedList', 'blockquote', 'attachFiles'],
'maxContentWidth' => '5xl',
],
'marketing' => [
'toolbarButtons' => ['h1', 'h2', 'bold', 'italic', 'alignLeft', 'alignCenter', 'link', 'media', 'oembed', 'horizontalRule'],
],
],
Then ->profile('marketing') in the form. This keeps content authors inside a controlled set of tags — which matters later at render time.
Shared form schema object. For a CMS with Post, Page, and LandingPage, extract the field into an invokable schema class (App\Filament\Schemas\ContentField) so a toolbar change is one edit, not five.
Media and attachments. Configure the disk once in config, and confirm the disk has a public URL and that php artisan storage:link has run. In production this is the single most common "images upload but render broken" cause.
TiptapEditor::make('body')
->fileAttachmentsDisk('s3-public')
->fileAttachmentsDirectory('editor/'.tenant()->id)
->fileAttachmentsVisibility('public');
Per-tenant directories are worth doing from day one — retrofitting them means rewriting stored URLs in old content.
Rendering side. Don't {!! $post->body !!} blindly. Either sanitize with mews/purifier (HTMLPurifier with a Tiptap-shaped allowlist) on output, or use the package's rich-content renderer, which is the better option if you use blocks, oEmbed, or mentions — those need the renderer to turn JSON node data into markup. Wrap that in a single <x-rich-content :content="$post->body" /> component so the escape hatch lives in one auditable place.
Blocks for structured content. Register block classes in config, add the blocks tool to the profile, and create the matching Blade view per block. Treat block output as a design-system contract: any block that emits raw user HTML needs its own sanitization.
Testing. These are normal Filament form fields, so Livewire tests are straightforward:
livewire(EditPost::class, ['record' => $post->id])
->fillForm(['body' => '<p>Hello</p>'])
->call('save')
->assertHasNoFormErrors();
$this->assertStringContainsString('<p>Hello</p>', $post->refresh()->body);
Assert on the DB string, not the rendered editor — Tiptap normalizes HTML in the browser, so client-side round-trips can reorder attributes.
public/js/ksquaredcoding/filament-tiptap-editor/. php artisan filament:assets && php artisan view:clear fixes it; CI/deploy scripts must include it or your first post-deploy bug report is guaranteed.^4.1 explicitly in composer.json — ^4.0 may resolve to a 4.0.x that constrains Filament to 4.x and produce a confusing composer why-not conflict with the rest of your Filament 5 plugins. Conversely, if any other editor plugin is still 4.x-only, you'll need to wait or drop it; version conflicts here surface as an unresolvable dependency tree, not a runtime error.toolbarButtons ('bulletlist') doesn't throw — the button simply doesn't render. When a tool "disappears", check the config string before the JS.array/AsArrayObject and add a renderer. Decide and commit early; HTML is the low-friction default.public → S3 later means backfilling files and keeping the same disk name, or rewriting stored HTML.->dehydrated(false) or a plain renderer instead of mounting a full editor.->tools() for one-off trims → named profiles in config for reuse → custom Tiptap extensions/buttons registered in config + a published view when you genuinely need new editor behavior. The last one couples you to the package's JS build, so budget for it.How can I help you explore Laravel packages today?