Filament Plugins

Purchase

TipTap editor

If you are using the Filament TipTap Editor package in your project, you can wire it up to the Media Library's file picker so that your users can insert images from the Media Library directly into the editor – instead of uploading files through TipTap's default file upload dialog.

The Media Library does not ship a dedicated TipTap extension. The integration is built using TipTap's own mediaAction() extension point: you create a custom Filament action that opens a MediaPicker modal, then pass that action class to the TiptapEditor component. The rest of TipTap's insert-from-action mechanism takes care of inserting the selected image into the editor.

This page covers the filament-tiptap-editor package by awcodes. It is separate from Filament's built-in RichEditor – if you are using that, see the Filament RichEditor integration instead.

Setup

1

Create a custom media action

Create a new action class that extends Filament\Forms\Components\Actions\Action. This class replaces TipTap's default file-upload dialog with a Media Library picker.

The action must:

  1. Use the name filament_tiptap_media – TipTap looks up actions by this exact name.
  2. Show a MediaPicker component in the modal form.
  3. Resolve the selected file's URL using the MediaPlugin's file attachment provider, then dispatch insertFromAction so TipTap inserts the image.
app/Filament/Actions/TiptapMediaLibraryAction.php
<?php

namespace App\Filament\Actions;

use Filament\Forms\Components\Actions\Action;
use Filament\Forms\Components\TextInput;
use FilamentTiptapEditor\TiptapEditor;
use RalphJSmit\Filament\Explore\Enums\FileType;
use RalphJSmit\Filament\MediaLibrary\Filament\Forms\Components\MediaPicker;
use RalphJSmit\Filament\MediaLibrary\Filament\Forms\Components\RichEditor\Plugins\MediaPlugin;

class TiptapMediaLibraryAction extends Action
{
    public static function getDefaultName(): ?string
    {
        return 'filament_tiptap_media';
    }

    protected function setUp(): void
    {
        parent::setUp();

        $plugin = MediaPlugin::make();

        $this
            ->arguments([
                'src' => '',
                'alt' => '',
            ])
            ->modalWidth('lg')
            ->modalHeading('Insert image from Media Library')
            ->fillForm(function (array $arguments) use ($plugin): array {
                $fileKey = $arguments['src'] ?? null;

                return [
                    'file' => $fileKey,
                    'alt' => $arguments['alt'] ?? null,
                ];
            })
            ->form([
                MediaPicker::make('file')
                    ->label('Image')
                    ->required(),
                TextInput::make('alt')
                    ->label('Alt text'),
            ])
            ->action(function (TiptapEditor $component, array $data) use ($plugin): void {
                $fileData = $plugin->getDriver()->findFile(FileType::File, (string) $data['file']);

                $src = $fileData
                    ? $plugin->getFileAttachmentProvider()->resolveUrl($fileData)
                    : null;

                $component->getLivewire()->dispatch(
                    event: 'insertFromAction',
                    type: 'media',
                    statePath: $component->getStatePath(),
                    media: [
                        'src' => $src,
                        'alt' => $data['alt'] ?? null,
                        'title' => null,
                        'width' => null,
                        'height' => null,
                        'lazy' => false,
                        'link_text' => null,
                    ],
                );
            });
    }
}

The URL returned by resolveUrl() is always fresh – for private S3 buckets it will be a signed temporary URL. Because TipTap stores the src directly in its output, images inserted this way will eventually have stale URLs if you are using private storage. For long-lived content on private disks, consider post-processing the stored JSON/HTML to refresh URLs before rendering, or use public storage.

2

Register the action on the editor

Pass your custom action class to the TiptapEditor component using the mediaAction() method:

app/Filament/Resources/PostResource.php
use App\Filament\Actions\TiptapMediaLibraryAction;
use FilamentTiptapEditor\TiptapEditor;

TiptapEditor::make('content')
    ->mediaAction(TiptapMediaLibraryAction::class)

When a user clicks the image toolbar button, TipTap will mount TiptapMediaLibraryAction instead of its default MediaAction, opening the Media Library picker modal.

Limitations vs. the RichEditor integration

The TipTap integration described on this page works differently from the Filament RichEditor integration, and there are a few things to be aware of:

URLs are stored verbatim. The RichEditor integration stores only the Media Library item's ID and resolves the URL dynamically every time the content is loaded or rendered. TipTap's insertFromAction mechanism stores the src URL directly in the editor output. This means:

  • URLs are not automatically refreshed when content is loaded or rendered.
  • Images served from private disks with temporary URLs will expire after the signed URL's validity window.

If your project uses public storage this is not a concern. For private storage, you need to handle URL refreshing yourself when displaying stored content.

No conversion picker. The RichEditor integration includes a conversion selector (when Spatie MediaLibrary conversions are configured), letting users choose which image size to insert. The TipTap integration shown here does not include this – the resolveUrl() call always returns the original-size URL. You can extend TiptapMediaLibraryAction to add a Select component and pass the conversion name as a second argument to resolveUrl($fileData, $conversion) if you need this.

Edit-image action is separate. TipTap registers a second action (filament_tiptap_edit_media) for editing an already-inserted image. The setup above does not customise that action. If you want the edit-image flow to also use the Media Library picker, create a second custom action class and register it with ->editMediaAction(YourEditAction::class).

© FilamentPlugins.com ✦ 2022 – 2026
PrivacyTerms & Conditions
All rights reserved.