Introduction
The rich editor allows you to edit and preview HTML content, as well as upload images. It uses TipTap as the underlying editor.Storing content as JSON
By default, the rich editor stores content as HTML. If you would like to store the content as JSON instead, you can use thejson() method:
array cast to the model property:
Customizing the toolbar buttons
You may set the toolbar buttons for the editor using thetoolbarButtons() method. The options shown here are the defaults:
h1- Applies the βh1β tag to the text.alignJustify- Justifies the text.clearFormatting- Clears all formatting from the selected text.details- Inserts a<details>tag, which allows users to create collapsible sections in their content.grid- Inserts a grid layout into the editor, allowing users to create responsive columns of content.gridDelete- Deletes the current grid layout.highlight- Highlights the selected text with a<mark>tag around it.horizontalRule- Inserts a horizontal rule.lead- Applies aleadclass around the text, which is typically used for the first paragraph of an article.small- Applies the<small>tag to the text, which is typically used for small print or disclaimers.code- Format the selected text as inline code.textColor- Changes the text color of the selected text.table- Creates a table in the editor with a default layout of 3 columns and 2 rows, with the first row configured as a header row.tableAddColumnBefore- Adds a new column before the current column.tableAddColumnAfter- Adds a new column after the current column.tableDeleteColumn- Deletes the current column.tableAddRowBefore- Adds a new row above the current row.tableAddRowAfter- Adds a new row below the current row.tableDeleteRow- Deletes the current row.tableMergeCells- Merges the selected cells into one cell.tableSplitCell- Splits the selected cell into multiple cells.tableToggleHeaderRow- Toggles the header row of the table.tableToggleHeaderCell- Toggles the header cell of the table.tableDelete- Deletes the table.
As well as allowing a static value, the toolbarButtons() method also accepts a function to dynamically calculate it. You can inject various utilities into the function as parameters.
toolbarButtons() method also accepts a function to dynamically calculate it. You can inject various utilities into the function as parameters.Learn more about utility injection.
Field
$component
Filament\Forms\Components\Field
The current field component instance.
Get function
$get
Filament\Schemas\Components\Utilities\Get
A function for retrieving values from the current form data. Validation is not run.
Livewire
$livewire
Livewire\Component
The Livewire component instance.
Eloquent model FQN
$model
?string<Illuminate\Database\Eloquent\Model>
The Eloquent model FQN for the current schema.
Operation
$operation
string
The current operation being performed by the schema. Usually
create, edit, or view.Raw state
$rawState
mixed
The current value of the field, before state casts were applied. Validation is not run.
Eloquent record
$record
?Illuminate\Database\Eloquent\Model
The Eloquent record for the current schema.
State
$state
mixed
The current value of the field. Validation is not run.
Customizing floating toolbars
If your toolbar is too full, you can use a floating toolbar to show certain tools in a toolbar below the cursor, only when the user is inside a specific node type. This allows you to keep the main toolbar clean while still providing access to additional tools when needed. You can customize the floating toolbars that appear when your cursor is placed inside a specific node by using thefloatingToolbars() method.
In the example below, a floating toolbar appears when the cursor is inside a paragraph node. It shows bold, italic, and similar buttons. When the cursor is in a heading node, it displays heading-related buttons, and when inside a table, it shows table-specific controls.
Customizing text colors
The rich editor includes a text color tool for styling inline text. By default, it uses the Tailwind CSS color palette. In light mode, the 600 shades are applied to text, and in dark mode, the 400 shades are used. You can customize which colors are available in the picker using thetextColors() method:
TextColor object to define the color:
TextColor::getDefaults() array:
TextColor object, the key of the array becomes the stored data-color attribute on the <span> tag, allowing you to reference the color in your CSS if needed. When you use the color as the array values, the actual color value (e.g., a HEX string) is stored as the data-color attribute.
You can also pass textColors() to the content renderer and rich content attribute so that server-side rendering matches your editor configuration.
You can also allow users to pick custom colors that arenβt in the predefined list by using the customTextColors() method:
customTextColors() on the content renderer, as it will automatically render any custom colors that are used in the content.
Rendering rich content
If youβre storing content as JSON instead of HTML, or your content requires processing to inject private image URLs or similar, youβll need to use theRichContentRenderer tool in Filament to output HTML:
toHtml() method returns a string. If you would like to output HTML in a Blade view without escaping the HTML, you can echo the RichContentRender object without calling toHtml():
Styling the rendered content
The rich editor HTML uses a combination of HTML elements, CSS classes, and inline styles to style the content, depending on the features used in the editor. If you render the content in a Filament table column or infolist entry withprose(), Filament will automatically apply the necessary styles for you. If you are outputting the content in your own Blade view, you may need to add some additional styles to ensure that the content is styled correctly.
One way of styling the content is to use Tailwind CSS Typography. This plugin provides a set of pre-defined styles for common HTML elements, such as headings, paragraphs, lists, and tables. You can apply these styles to a container element using the prose class:
fi-prose CSS class that adds these additional styles. Any app that loads Filamentβs vendor/filament/support/resources/css/index.css CSS will have access to this class. The styling is different to the prose class, but fits with Filamentβs design system better:
Security
By default, the editor outputs raw HTML, and sends it to the backend. Attackers are able to intercept the value of the component and send a different raw HTML string to the backend. As such, it is important that when outputting the HTML from a rich editor, it is sanitized; otherwise your site may be exposed to Cross-Site Scripting (XSS) vulnerabilities. When Filament outputs raw HTML from the database in components such asTextColumn and TextEntry, it sanitizes it to remove any dangerous JavaScript. However, if you are outputting the HTML from a rich editor in your own Blade view, this is your responsibility. One option is to use Filamentβs sanitizeHtml() helper to do this, which is the same tool we use to sanitize HTML in the components mentioned above:
Uploading images to the editor
By default, uploaded images are stored publicly on your storage disk, so that the rich content stored in the database can be output easily anywhere. You may customize how images are uploaded using configuration methods:As well as allowing static values, the fileAttachmentsDisk(), fileAttachmentsDirectory(), and fileAttachmentsVisibility() methods also accept functions to dynamically calculate them. You can inject various utilities into the function as parameters.
fileAttachmentsDisk(), fileAttachmentsDirectory(), and fileAttachmentsVisibility() methods also accept functions to dynamically calculate them. You can inject various utilities into the function as parameters.Learn more about utility injection.
Field
$component
Filament\Forms\Components\Field
The current field component instance.
Get function
$get
Filament\Schemas\Components\Utilities\Get
A function for retrieving values from the current form data. Validation is not run.
Livewire
$livewire
Livewire\Component
The Livewire component instance.
Eloquent model FQN
$model
?string<Illuminate\Database\Eloquent\Model>
The Eloquent model FQN for the current schema.
Operation
$operation
string
The current operation being performed by the schema. Usually
create, edit, or view.Raw state
$rawState
mixed
The current value of the field, before state casts were applied. Validation is not run.
Eloquent record
$record
?Illuminate\Database\Eloquent\Model
The Eloquent record for the current schema.
State
$state
mixed
The current value of the field. Validation is not run.
Using private images in the editor
Using private images in the editor adds a layer of complexity to the process, since private images cannot be accessed directly via a permanent URL. Each time the editor is loaded or its content is rendered, temporary URLs need to be generated for each image, which are never stored in the database. Instead, Filament adds adata-id attribute to the image tags, which contains an identifier for the image in the storage disk, so that a temporary URL can be generated on demand.
When rendering the content using private images, ensure that you are using the RichContentRenderer tool in Filament to output HTML:
Validating uploaded images
You may use thefileAttachmentsAcceptedFileTypes() method to control a list of accepted mime types for uploaded images. By default, image/png, image/jpeg, image/gif, and image/webp are accepted:
fileAttachmentsMaxSize() method to control the maximum file size for uploaded images. The size is specified in kilobytes. By default, the maximum size is 12288 KB (12 MB):
Allowing users to resize images
By default, images in the editor cannot be resized by the user. You may enable image resizing using theresizableImages() method:
As well as allowing a static value, the resizableImages() method also accepts a function to dynamically calculate it. You can inject various utilities into the function as parameters.
resizableImages() method also accepts a function to dynamically calculate it. You can inject various utilities into the function as parameters.Learn more about utility injection.
Field
$component
Filament\Forms\Components\Field
The current field component instance.
Get function
$get
Filament\Schemas\Components\Utilities\Get
A function for retrieving values from the current form data. Validation is not run.
Livewire
$livewire
Livewire\Component
The Livewire component instance.
Eloquent model FQN
$model
?string<Illuminate\Database\Eloquent\Model>
The Eloquent model FQN for the current schema.
Operation
$operation
string
The current operation being performed by the schema. Usually
create, edit, or view.Raw state
$rawState
mixed
The current value of the field, before state casts were applied. Validation is not run.
Eloquent record
$record
?Illuminate\Database\Eloquent\Model
The Eloquent record for the current schema.
State
$state
mixed
The current value of the field. Validation is not run.
Using custom blocks
Custom blocks are elements that users can drag and drop into the rich editor. You can define custom blocks that user can insert into the rich editor using thecustomBlocks() method:
Filament\Forms\Components\RichEditor\RichContentCustomBlock class. The getId() method should return a unique identifier for the block, and the getLabel() method should return the label that will be displayed in the editorβs side panel:
configureEditorAction() method to configure the modal that will be opened when the block is inserted:
schema() method on the action can define form fields that will be displayed in the modal. When the user submits the form, the form data will be saved as βconfigurationβ for that block.
Rendering a preview for a custom block
Once a block is inserted into the editor, you may define a βpreviewβ for it using thetoPreviewHtml() method. This method should return a string of HTML that will be displayed in the editor when the block is inserted, allowing users to see what the block will look like before they save it. You can access the $config for the block in this method, which contains the data that was submitted in the modal when the block was inserted:
getPreviewLabel() can be defined if you would like to customize the label that is displayed above the preview in the editor. By default, it will use the label defined in the getLabel() method, but the getPreviewLabel() is able to access the $config for the block, allowing you to display dynamic information in the label:
Rendering content with custom blocks
When rendering the rich content, you can pass the array of custom blocks to theRichContentRenderer to ensure that the blocks are rendered correctly:
toHtml() method that returns the HTML that should be rendered for that block:
toHtml() method receives two parameters: $config, which contains the configuration data submitted in the modal when the block was inserted, and $data, which contains any additional data that may be needed to render the block. This allows you to access the configuration data and render the block accordingly. The data can be passed in the customBlocks() method:
Opening the custom blocks panel by default
If you want the custom blocks panel to be open by default when the rich editor is loaded, you can use theactivePanel('customBlocks') method:
Using merge tags
Merge tags allow the user to insert βplaceholdersβ into their rich content, which can be replaced with dynamic values when the content is rendered. This is useful for inserting things like the current userβs name, or the current date. To register merge tags on an editor, use themergeTags() method:
{{ name }}. When the content is rendered, these tags will be replaced with the corresponding values.
To insert a merge tag into the content, users can start typing {{ to search for a tag to insert. Alternatively, they can click on the βmerge tagsβ tool in the editorβs toolbar, which opens a panel containing all the merge tags. They can then drag a merge tag from the editorβs side panel into the content or click to insert it.
Rendering content with merge tags
When rendering the rich content, you can pass an array of values to replace the merge tags with:Using HTML content in merge tags
By default, merge tags render their values as plain text. However, you can render HTML content in merge tags by providing values that implement LaravelβsHtmlable interface. This is useful for inserting formatted content, links, or other HTML elements:
Htmlable interface (such as HtmlString), the system automatically detects this and renders the HTML content without escaping it. Non-Htmlable values continue to be rendered as plain text for security.
Using custom merge tag labels
You may provide custom labels for merge tags that will be displayed in the editorβs side panel and content preview using an associative array where the keys are the merge tag names and the values are the labels:Opening the merge tags panel by default
If you want the merge tags panel to be open by default when the rich editor is loaded, you can use theactivePanel('mergeTags') method:
Using mentions
Mentions allow users to insert references to other records (such as users, issues, or tags) by typing a trigger character. When the user types a trigger character like@, a dropdown appears allowing them to search and select from available options. The selected mention is inserted as a non-editable inline token.
To register mentions on an editor, use the mentions() method with one or more MentionProvider instances:
make()) that activates the mention search. You can have multiple providers with different triggers:
Searching mentions from the database
For large datasets, you should fetch results dynamically usinggetSearchResultsUsing(). The callback receives the search term and should return an array of options with the format [id => label].
When using dynamic search results, only the mentionβs id is stored in the content. To display the correct label when the content is loaded, you must also provide getLabelsUsing(). This callback receives an array of IDs and should return an array with the format [id => label]:
Rendering content with mentions
When rendering the rich content, you can pass the array of mention providers to theRichContentRenderer to ensure that the mentions are rendered correctly.
You can make mentions link to a URL when rendered using the url() method. The callback receives the mentionβs id and label, and should return a URL string:
Registering rich content attributes
There are elements of the rich editor configuration that apply to both the editor and the renderer. For example, if you are using private images, custom blocks, merge tags, mentions, or plugins, you need to ensure that the same configuration is used in both places. To do this, Filament provides you with a way to register rich content attributes that can be used in both the editor and the renderer. If a plugin implementsHasFileAttachmentProvider, the file attachment provider is automatically resolved from the plugin, so you do not need to call fileAttachmentProvider() on the attribute or on the renderer.
To register rich content attributes on an Eloquent model, you should use the InteractsWithRichContent trait and implement the HasRichContent interface. This allows you to register the attributes in the setUpRichContent() method:
RichEditor component, the configuration registered for the corresponding attribute will be used:
renderRichContent() method on the model, passing the name of the attribute:
Htmlable object to render without escaping the HTML:
Extending the rich editor
You can create plugins for the rich editor, which allow you to add custom TipTap extensions to the editor and renderer, as well as custom toolbar buttons. Create a new class that implements theRichContentPlugin interface:
plugins() method to register your plugin with the rich editor and rich content renderer:
Enabling or disabling toolbar buttons from a plugin
By default, when a plugin provides tools viagetEditorTools(), those tools are registered but not automatically shown in the toolbar. The user needs to manually add them using toolbarButtons() or enableToolbarButtons().
If you want your plugin to automatically enable or disable toolbar buttons, you can implement the HasToolbarButtons interface alongside RichContentPlugin. This is an optional, separate interface:
getEnabledToolbarButtons() method returns button names to add to the toolbar. The getDisabledToolbarButtons() method returns button names to remove from the toolbar.
Plugin toolbar modifications are applied before user-level modifications. This means the user can always override the pluginβs behavior using enableToolbarButtons() or disableToolbarButtons():
Setting up a TipTap JavaScript extension
Filament is able to asynchronously load JavaScript extensions for TipTap. To do this, you need to create a JavaScript file that contains the extension, and register it in thegetTipTapJsExtensions() method of your plugin.
For instance, if you wanted to use the TipTap highlight extension, make sure it is installed first:
highlight.js in the resources/js/filament/rich-content-plugins directory, and add the following code to it:
npm:
bin/build.js:
resources/js/filament/rich-content-plugins/highlight.js into resources/js/dist/filament/rich-content-plugins/highlight.js. You can change these paths to suit your needs. You can compile as many files as you want.
To run the script and compile this file into resources/js/dist/filament/rich-content-plugins/highlight.js run the following command:
boot() method of a service provider, like AppServiceProvider, and use loadedOnRequest() so that it is not downloaded until the rich editor is loaded on a page:
/public directory of your app so that it can be served, you can use the filament:assets command:
getTipTapJsExtensions() method should return the path to the JavaScript file you just created. Now that itβs registered with FilamentAsset, you can use the getScriptSrc() method to get the URL to the file: