forms Documentation

Official TypeScript developer resources and documentation for filamentphp/forms. Verified technical context for AI.

0 stars TypeScript TypeScript 1 file ยท ~29,370 tokens
RAW Doc

File: docs/02-getting-started.md


title: Getting started

Preparing your Livewire component

Implement the HasForms interface and use the InteractsWithForms trait:

php
<?php

namespace App\Http\Livewire;

use Filament\Forms;
use Illuminate\Contracts\View\View;
use Livewire\Component;

class EditPost extends Component implements Forms\Contracts\HasForms
{
    use Forms\Concerns\InteractsWithForms;
    
    public function render(): View
    {
        return view('edit-post');
    }
}

In your Livewire component's view, render the form:

blade
<form wire:submit.prevent="submit">
    {{ $this->form }}
    
    
        Submit
    
</form>

Finally, add any fields and layout components to the Livewire component's getFormSchema() method:

php
<?php

namespace App\Http\Livewire;

use Filament\Forms;
use Illuminate\Contracts\View\View;
use Livewire\Component;

class EditPost extends Component implements Forms\Contracts\HasForms
{
    use Forms\Concerns\InteractsWithForms;
    
    public Post $post;
    
    public $title;
    public $content;
    
    public function mount(): void
    {
        $this->form->fill([
            'title' => $this->post->title,
            'content' => $this->post->content,
        ]);
    }
    
    protected function getFormSchema(): array
    {
        return [
            Forms\Components\TextInput::make('title')->required(),
            Forms\Components\MarkdownEditor::make('content'),
            // ...
        ];
    }
    
    public function submit(): void
    {
        // ...
    }
    
    public function render(): View
    {
        return view('edit-post');
    }
}

Visit your Livewire component in the browser, and you should see the form components from getFormSchema().

Initializing forms

You must initialize forms when the Livewire component is first loaded. This is done with the fill() form method, often called in the mount() method of the Livewire component.

For your fields to hold data, they should have a corresponding property on your Livewire component, just as in Livewire normally.

php
<?php

namespace App\Http\Livewire;

use App\Models\Post;
use Filament\Forms;
use Illuminate\Contracts\View\View;
use Livewire\Component;

class CreatePost extends Component implements Forms\Contracts\HasForms
{
    use Forms\Concerns\InteractsWithForms;
    
    public $title = '';
    public $content = '';
    
    public function mount(): void
    {
        $this->form->fill();
    }
    
    protected function getFormSchema(): array
    {
        return [
            Forms\Components\TextInput::make('title')
                ->default('Status Update')
                ->required(),
            Forms\Components\MarkdownEditor::make('content'),
        ];
    }
    
    public function render(): View
    {
        return view('create-post');
    }
}

You may customize what happens after fields are filled using the afterStateHydrated() method.

Filling forms with data

To fill a form with data, call the fill() method on your form, and pass an array of data to fill it with:

php
<?php

namespace App\Http\Livewire;

use App\Models\Post;
use Filament\Forms;
use Illuminate\Contracts\View\View;
use Livewire\Component;

class EditPost extends Component implements Forms\Contracts\HasForms
{
    use Forms\Concerns\InteractsWithForms;
    
    public Post $post;
    
    public $title;
    public $content;
    
    public function mount(): void
    {
        $this->form->fill([
            'title' => $this->post->title,
            'content' => $this->post->content,
        ]);
    }
    
    protected function getFormSchema(): array
    {
        return [
            Forms\Components\TextInput::make('title')->required(),
            Forms\Components\MarkdownEditor::make('content'),
        ];
    }
    
    public function render(): View
    {
        return view('edit-post');
    }
}

Getting data from forms

To get all form data in an array, call the getState() method on your form.

php
<?php

namespace App\Http\Livewire;

use App\Models\Post;
use Filament\Forms;
use Illuminate\Contracts\View\View;
use Livewire\Component;

class CreatePost extends Component implements Forms\Contracts\HasForms
{
    use Forms\Concerns\InteractsWithForms;
    
    public $title = '';
    public $content = '';
    
    public function mount(): void
    {
        $this->form->fill();
    }
    
    protected function getFormSchema(): array
    {
        return [
            Forms\Components\TextInput::make('title')->required(),
            Forms\Components\MarkdownEditor::make('content'),
        ];
    }
    
    public function create(): void
    {
        Post::create($this->form->getState());
    }
    
    public function render(): View
    {
        return view('create-post');
    }
}

When getState() is run:

  1. Validation rules are checked, and if errors are present, the form is not submitted.
  2. Any pending file uploads are stored permanently in the filesystem.
  3. Field relationships, if they are defined, are saved.

You may transform the value that is dehydrated from a field using the dehydrateStateUsing() method.

Registering a model

You may register a model to a form. The form builder is able to use this model to unlock DX features, such as:

  • Automatically retrieving the database table name when using database validation rules like exists and unique.
  • Automatically attaching relationships to the model when the form is saved, when using fields such as the Select, Repeater, SpatieMediaLibraryFileUpload, or SpatieTagsInput.

Pass a model instance to a form using the getFormModel() method:

php
<?php

namespace App\Http\Livewire;

use App\Models\Post;
use Filament\Forms;
use Illuminate\Contracts\View\View;
use Livewire\Component;

class EditPost extends Component implements Forms\Contracts\HasForms
{
    use Forms\Concerns\InteractsWithForms;
    
    public Post $post;
    
    public $title;
    public $content;
    public $tags;
    
    public function mount(): void
    {
        $this->form->fill([
            'title' => $this->post->title,
            'content' => $this->post->content,
        ]);
    }
    
    protected function getFormSchema(): array
    {
        return [
            Forms\Components\TextInput::make('title')->required(),
            Forms\Components\MarkdownEditor::make('content'),
            Forms\Components\SpatieTagsInput::make('tags'),
        ];
    }
    
    protected function getFormModel(): Post
    {
        return $this->post;
    }
    
    public function render(): View
    {
        return view('edit-post');
    }
}

Alternatively, you may pass the model instance to the field that requires it directly, using the model() method:

php
<?php

namespace App\Http\Livewire;

use App\Models\Post;
use Filament\Forms;
use Illuminate\Contracts\View\View;
use Livewire\Component;

class EditPost extends Component implements Forms\Contracts\HasForms
{
    use Forms\Concerns\InteractsWithForms;
    
    public Post $post;
    
    public $title;
    public $content;
    public $tags;
    
    public function mount(): void
    {
        $this->form->fill([
            'title' => $this->post->title,
            'content' => $this->post->content,
        ]);
    }
    
    protected function getFormSchema(): array
    {
        return [
            Forms\Components\TextInput::make('title')->required(),
            Forms\Components\MarkdownEditor::make('content'),
            Forms\Components\SpatieTagsInput::make('tags')->model($this->post),
        ];
    }
    
    public function render(): View
    {
        return view('edit-post');
    }
}

You may now use field relationships;

Registering a model class

In some cases, the model instance is not available until the form has been submitted. For example, in a form that creates a post, the post model instance cannot be passed to the form before it has been submitted.

You may receive some of the same benefits of registering a model by registering its class instead:

php
<?php

namespace App\Http\Livewire;

use App\Models\Post;
use Filament\Forms;
use Illuminate\Contracts\View\View;
use Livewire\Component;

class CreatePost extends Component implements Forms\Contracts\HasForms
{
    use Forms\Concerns\InteractsWithForms;
    
    public $title = '';
    public $content = '';
    public $categories = [];
    
    public function mount(): void
    {
        $this->form->fill();
    }
    
    protected function getFormSchema(): array
    {
        return [
            Forms\Components\TextInput::make('title')
                ->required(),
            Forms\Components\MarkdownEditor::make('content'),
            Forms\Components\Select::make('categories')
                ->multiple()
                ->relationship('categories', 'name'),
        ];
    }
    
    protected function getFormModel(): string
    {
        return Post::class;
    }
    
    public function render(): View
    {
        return view('create-post');
    }
}

You may now use field relationships.

Field relationships

Some fields, such as the Select, Repeater, SpatieMediaLibraryFileUpload, or SpatieTagsInput are able to interact with model relationships.

For example, Select can be used to attach multiple records to a BelongstoMany relationship. When registering a model to the form or component, these relationships will be automatically saved to the pivot table when getState() is called:

php
<?php

namespace App\Http\Livewire;

use App\Models\Post;
use Filament\Forms;
use Illuminate\Contracts\View\View;
use Livewire\Component;

class EditPost extends Component implements Forms\Contracts\HasForms
{
    use Forms\Concerns\InteractsWithForms;
    
    public Post $post;
    
    public $title;
    public $content;
    public $categories;
    
    public function mount(): void
    {
        $this->form->fill([
            'title' => $this->post->title,
            'content' => $this->post->content,
        ]);
    }
    
    protected function getFormSchema(): array
    {
        return [
            Forms\Components\TextInput::make('title')
                ->required(),
            Forms\Components\MarkdownEditor::make('content'),
            Forms\Components\Select::make('categories')
                ->multiple()
                ->relationship('categories', 'name'),
        ];
    }
    
    protected function getFormModel(): Post
    {
        return $this->post;
    }
    
    public function save(): void
    {
        $this->post->update(
            $this->form->getState(),
        );
    }
    
    public function render(): View
    {
        return view('edit-post');
    }
}

Saving field relationships manually

In some cases, the model instance is not available until the form has been submitted. For example, in a form that creates a post, the post model instance cannot be passed to the form before it has been submitted. In this case, you will pass the model class instead, but any field relationships will need to be saved manually after.

In this situation, you may call the model() and saveRelationships() methods on the form after the instance has been created:

php
<?php

namespace App\Http\Livewire;

use App\Models\Post;
use Filament\Forms;
use Illuminate\Contracts\View\View;
use Illuminate\Database\Eloquent\Model;
use Livewire\Component;

class CreatePost extends Component implements Forms\Contracts\HasForms
{
    use Forms\Concerns\InteractsWithForms;
    
    public $title = '';
    public $content = '';
    public $tags = [];
    
    public function mount(): void
    {
        $this->form->fill();
    }
    
    protected function getFormSchema(): array
    {
        return [
            Forms\Components\TextInput::make('title')->required(),
            Forms\Components\MarkdownEditor::make('content'),
            Forms\Components\SpatieTagsInput::make('tags'),
        ];
    }
    
    protected function getFormModel(): string
    {
        return Post::class;
    }
    
    public function create(): void
    {
        $post = Post::create($this->form->getState());
        
        $this->form->model($post)->saveRelationships();
    }
    
    public function render(): View
    {
        return view('create-post');
    }
}

Saving relationships when the field is hidden

By default, relationships will only be saved if the field is visible. For example, if you have a Repeater field that is only visible on a certain condition, the relationships will not be saved when it is hidden.

This might cause unexpected behaviour if you still want to save the relationship, even when the field is hidden. To force relationships to be saved, you may call the saveRelationshipsWhenHidden() method on the form component:

php
use Filament\Forms\Components\SpatieMediaLibraryFileUpload;

SpatieMediaLibraryFileUpload::make('attachments')
    ->visible(fn (Closure $get): bool => $get('has_attachments'))
    ->saveRelationshipsWhenHidden();

Using multiple forms

By default, the InteractsWithForms trait only handles one form per Livewire component. To change this, you can override the getForms() method to return more than one form, each with a unique name:

text
/* Detailed source-code truncated for AI context efficiency. */

Scoping form data to an array property

You may scope the entire form data to a single array property on your Livewire component. This will allow you to avoid having to define a new property for each field:

php
<?php

namespace App\Http\Livewire;

use App\Models\Post;
use Filament\Forms;
use Illuminate\Contracts\View\View;
use Livewire\Component;

class EditPost extends Component implements Forms\Contracts\HasForms
{
    use Forms\Concerns\InteractsWithForms;
    
    public Post $post;
    
    public $data;
    
    public function mount(): void
    {
        $this->form->fill([
            'title' => $this->post->title,
            'content' => $this->post->content,
        ]);
    }
    
    protected function getFormSchema(): array
    {
        return [
            Forms\Components\TextInput::make('title')->required(),
            Forms\Components\MarkdownEditor::make('content'),
            Forms\Components\SpatieTagsInput::make('tags'),
        ];
    }
    
    protected function getFormModel(): Post
    {
        return $this->post;
    }
    
    protected function getFormStatePath(): string
    {
        return 'data';
    }
    
    public function render(): View
    {
        return view('edit-post');
    }
}

In this example, all data from your form will be stored in the $data array.


File: docs/03-fields.md


title: Fields

Getting started

Field classes can be found in the Filament\Form\Components namespace.

Fields reside within the schema of your form, alongside any layout components.

If you're using the fields in a Livewire component, you can put them in the getFormSchema() method:

php
protected function getFormSchema(): array
{
    return [
        // ...
    ];
}

If you're using them in admin panel resources or relation managers, you must put them in the $form->schema() method:

php
public static function form(Form $form): Form
{
    return $form
        ->schema([
            // ...
        ]);
}

Fields may be created using the static make() method, passing its name. The name of the field should correspond to a property on your Livewire component. You may use Livewire's "dot syntax" to bind fields to nested properties such as arrays and Eloquent models.

php
use Filament\Forms\Components\TextInput;

TextInput::make('name')

Setting a label

By default, the label of the field will be automatically determined based on its name. To override the field's label, you may use the label() method. Customizing the label in this way is useful if you wish to use a translation string for localization:

php
use Filament\Forms\Components\TextInput;

TextInput::make('name')->label(__('fields.name'))

Optionally, you can have the label automatically translated by using the translateLabel() method:

php
use Filament\Forms\Components\TextInput;

TextInput::make('name')->translateLabel() // Equivalent to `label(__('Name'))`

Setting an ID

In the same way as labels, field IDs are also automatically determined based on their names. To override a field ID, use the id() method:

php
use Filament\Forms\Components\TextInput;

TextInput::make('name')->id('name-field')

Setting a default value

Fields may have a default value. This will be filled if the form's fill() method is called without any arguments. To define a default value, use the default() method:

php
use Filament\Forms\Components\TextInput;

TextInput::make('name')->default('John')

Note that inside the admin panel this only works on Create Pages, as Edit Pages will always fill the data from the model.

Helper messages and hints

Sometimes, you may wish to provide extra information for the user of the form. For this purpose, you may use helper messages and hints.

Help messages are displayed below the field. The helperText() method supports Markdown formatting:

php
use Filament\Forms\Components\TextInput;

TextInput::make('name')->helperText('Your full name here, including any middle names.')

Hints can be used to display text adjacent to its label:

php
use Filament\Forms\Components\TextInput;

TextInput::make('password')->hint('[Forgotten your password?](forgotten-password)')

Hints may also have an icon rendered next to them:

php
use Filament\Forms\Components\RichEditor;

RichEditor::make('content')
    ->hint('Translatable')
    ->hintIcon('heroicon-s-translate')

Hints may have a color(). By default it's gray, but you may use primary, success, warning, or danger:

php
use Filament\Forms\Components\RichEditor;

RichEditor::make('content')
    ->hint('Translatable')
    ->hintColor('primary')

Custom attributes

The HTML attributes of the field's wrapper can be customized by passing an array of extraAttributes():

php
use Filament\Forms\Components\TextInput;

TextInput::make('name')->extraAttributes(['title' => 'Text input'])

To add additional HTML attributes to the input itself, use extraInputAttributes():

php
use Filament\Forms\Components\Select;

Select::make('categories')
    ->extraInputAttributes(['multiple' => true])

Disabling

You may disable a field to prevent it from being edited:

php
use Filament\Forms\Components\TextInput;

TextInput::make('name')->disabled()

Optionally, you may pass a boolean value to control if the field should be disabled or not:

php
use Filament\Forms\Components\Toggle;

Toggle::make('is_admin')->disabled(! auth()->user()->isAdmin())

Please note that disabling a field does not prevent it from being saved, and a skillful user could manipulate the HTML of the page and alter its value.

To prevent a field from being saved, use the dehydrated(false) method:

php
Toggle::make('is_admin')->dehydrated(false)

Alternatively, you may only want to save a field conditionally, maybe if the user is an admin:

php
Toggle::make('is_admin')
    ->disabled(! auth()->user()->isAdmin())
    ->dehydrated(auth()->user()->isAdmin())

If you're using the admin panel and only want to save disabled fields on the Create page of a resource:

php
use Filament\Resources\Pages\CreateRecord;
use Filament\Resources\Pages\Page;

TextInput::make('slug')
    ->disabled()
    ->dehydrated(fn (Page $livewire) => $livewire instanceof CreateRecord)

Hiding

You may hide a field:

php
use Filament\Forms\Components\TextInput;

TextInput::make('name')->hidden()

Optionally, you may pass a boolean value to control if the field should be hidden or not:

php
use Filament\Forms\Components\TextInput;

TextInput::make('name')->hidden(! auth()->user()->isAdmin())

Autofocusing

Most fields will be autofocusable. Typically, you should aim for the first significant field in your form to be autofocused for the best user experience.

php
use Filament\Forms\Components\TextInput;

TextInput::make('name')->autofocus()

Setting a placeholder

Many fields will also include a placeholder value for when it has no value. You may customize this using the placeholder() method:

php
use Filament\Forms\Components\TextInput;

TextInput::make('name')->placeholder('John Doe')

Global settings

If you wish to change the default behaviour of a field globally, then you can call the static configureUsing() method inside a service provider's boot() method, to which you pass a Closure to modify the component using. For example, if you wish to make all checkboxes inline(false), you can do it like so:

php
use Filament\Forms\Components\Checkbox;

Checkbox::configureUsing(function (Checkbox $checkbox): void {
    $checkbox->inline(false);
});

Of course, you are still able to overwrite this on each field individually:

php
use Filament\Forms\Components\Checkbox;

Checkbox::make('is_admin')->inline()

Text input

The text input allows you to interact with a string:

php
use Filament\Forms\Components\TextInput;

TextInput::make('name')

You may set the type of string using a set of methods. Some, such as email(), also provide validation:

php
use Filament\Forms\Components\TextInput;

TextInput::make('text')
    ->email()
    ->numeric()
    ->password()
    ->tel()
    ->url()

You may instead use the type() method to pass another HTML input type:

php
use Filament\Forms\Components\TextInput;

TextInput::make('backgroundColor')->type('color')

You may limit the length of the input by setting the minLength() and maxLength() methods. These methods add both frontend and backend validation:

php
use Filament\Forms\Components\TextInput;

TextInput::make('name')
    ->minLength(2)
    ->maxLength(255)

You may also specify the exact length of the input by setting the length(). This method adds both frontend and backend validation:

php
use Filament\Forms\Components\TextInput;

TextInput::make('code')->length(8)

In addition, you may validate the minimum and maximum value of the input by setting the minValue() and maxValue() methods:

php
use Filament\Forms\Components\TextInput;

TextInput::make('number')
    ->numeric()
    ->minValue(1)
    ->maxValue(100)

You may set the autocomplete configuration for the text field using the autocomplete() method:

php
use Filament\Forms\Components\TextInput;

TextInput::make('password')
    ->password()
    ->autocomplete('new-password')

As a shortcut for autocomplete="off", you may disableAutocomplete():

php
use Filament\Forms\Components\TextInput;

TextInput::make('password')
    ->password()
    ->disableAutocomplete()

For more complex autocomplete options, text inputs also support datalists.

Phone number validation

When using a tel() field, the value will be validated using: /^[+]*[(]{0,1}[0-9]{1,4}[)]{0,1}[-\s\.\/0-9]*$/.

If you wish to change that, then you can use the telRegex() method:

php
use Filament\Forms\Components\TextInput;

TextInput::make('phone')
    ->tel()
    ->telRegex('/^[+]*[(]{0,1}[0-9]{1,4}[)]{0,1}[-\s\.\/0-9]*$/')

Alternatively, to customize the telRegex() across all fields, use a service provider:

php
use Filament\Forms\Components\TextInput;

TextInput::configureUsing(function (TextInput $component): void {
    $component->telRegex('/^[+]*[(]{0,1}[0-9]{1,4}[)]{0,1}[-\s\.\/0-9]*$/');
});

Affixes

You may place text before and after the input using the prefix() and suffix() methods:

php
use Filament\Forms\Components\TextInput;

TextInput::make('domain')
    ->url()
    ->prefix('https://')
    ->suffix('.com')

You may place a icon before and after the input using the prefixIcon() and suffixIcon() methods:

php
use Filament\Forms\Components\TextInput;

TextInput::make('domain')
    ->url()
    ->prefixIcon('heroicon-s-external-link')
    ->suffixIcon('heroicon-s-external-link')

You may render an action before and after the input using the prefixAction() and suffixAction() methods:

php
use Filament\Forms\Components\Actions\Action;
use Filament\Forms\Components\TextInput;

TextInput::make('domain')
    ->suffixAction(fn (?string $state): Action =>
        Action::make('visit')
            ->icon('heroicon-s-external-link')
            ->url(
                filled($state) ? "https://{$state}" : null,
                shouldOpenInNewTab: true,
            ),
    )

Input masking

Input masking is the practice of defining a format that the input value must conform to.

In Filament, you may interact with the Mask object in the mask() method to configure your mask:

php
use Filament\Forms\Components\TextInput;

TextInput::make('name')
    ->mask(fn (TextInput\Mask $mask) => $mask->pattern('+{7}(000)000-00-00'))

Under the hood, masking is powered by imaskjs. The vast majority of its masking features are also available in Filament. Reading their guide first, and then approaching the same task using Filament is probably the easiest option.

You may define and configure a numeric mask to deal with numbers:

php
use Filament\Forms\Components\TextInput;

TextInput::make('number')
    ->numeric()
    ->mask(fn (TextInput\Mask $mask) => $mask
        ->numeric()
        ->decimalPlaces(2) // Set the number of digits after the decimal point.
        ->decimalSeparator(',') // Add a separator for decimal numbers.
        ->integer() // Disallow decimal numbers.
        ->mapToDecimalSeparator([',']) // Map additional characters to the decimal separator.
        ->minValue(1) // Set the minimum value that the number can be.
        ->maxValue(100) // Set the maximum value that the number can be.
        ->normalizeZeros() // Append or remove zeros at the end of the number.
        ->padFractionalZeros() // Pad zeros at the end of the number to always maintain the maximum number of decimal places.
        ->thousandsSeparator(','), // Add a separator for thousands.
    )

Enum masks limit the options that the user can input:

php
use Filament\Forms\Components\TextInput;

TextInput::make('code')->mask(fn (TextInput\Mask $mask) => $mask->enum(['F1', 'G2', 'H3']))

Range masks can be used to restrict input to a number range:

php
use Filament\Forms\Components\TextInput;

TextInput::make('code')->mask(fn (TextInput\Mask $mask) => $mask
    ->range()
    ->from(1) // Set the lower limit.
    ->to(100) // Set the upper limit.
    ->maxValue(100), // Pad zeros at the start of smaller numbers.
)

In addition to simple pattens, you may also define multiple pattern blocks:

php
use Filament\Forms\Components\TextInput;

TextInput::make('cost')->mask(fn (TextInput\Mask $mask) => $mask
    ->patternBlocks([
        'money' => fn (Mask $mask) => $mask
            ->numeric()
            ->thousandsSeparator(',')
            ->decimalSeparator('.'),
    ])
    ->pattern('$money'),
)

There is also a money() method that is able to define easier formatting for currency inputs. This example, the symbol prefix is โ‚ฌ, there is a , thousands separator, and two decimal places:

php
use Filament\Forms\Components\TextInput;

TextInput::make('cost')->mask(fn (TextInput\Mask $mask) => $mask->money(prefix: 'โ‚ฌ', thousandsSeparator: ',', decimalPlaces: 2))

You can also control whether the number is signed or not. While the default is to allow both negative and positive numbers, isSigned: false allows only positive numbers:

php
use Filament\Forms\Components\TextInput;

TextInput::make('cost')->mask(fn (TextInput\Mask $mask) => $mask->money(prefix: 'โ‚ฌ', thousandsSeparator: ',', decimalPlaces: 2, isSigned: false))

Datalists

You may specify datalist options for a text input using the datalist() method:

php
TextInput::make('manufacturer')
    ->datalist([
        'BWM',
        'Ford',
        'Mercedes-Benz',
        'Porsche',
        'Toyota',
        'Tesla',
        'Volkswagen',
    ])

Datalists provide autocomplete options to users when they use a text input. However, these are purely recommendations, and the user is still able to type any value into the input. If you're looking for strictly predefined options, check out select fields.

Select

The select component allows you to select from a list of predefined options:

php
use Filament\Forms\Components\Select;

Select::make('status')
    ->options([
        'draft' => 'Draft',
        'reviewing' => 'Reviewing',
        'published' => 'Published',
    ])

In the options() array, the array keys are saved, and the array values will be the label of each option in the dropdown.

You may enable a search input to allow easier access to many options, using the searchable() method:

php
use Filament\Forms\Components\Select;

Select::make('authorId')
    ->label('Author')
    ->options(User::all()->pluck('name', 'id'))
    ->searchable()

If you have lots of options and want to populate them based on a database search or other external data source, you can use the getSearchResultsUsing() and getOptionLabelUsing() methods instead of options().

The getSearchResultsUsing() method accepts a callback that returns search results in $key => $value format.

The getOptionLabelUsing() method accepts a callback that transforms the selected option $value into a label.

php
Select::make('authorId')
    ->searchable()
    ->getSearchResultsUsing(fn (string $search) => User::where('name', 'like', "%{$search}%")->limit(50)->pluck('name', 'id'))
    ->getOptionLabelUsing(fn ($value): ?string => User::find($value)?->name),

You can prevent the placeholder from being selected using the disablePlaceholderSelection() method:

php
use Filament\Forms\Components\Select;

Select::make('status')
    ->options([
        'draft' => 'Draft',
        'reviewing' => 'Reviewing',
        'published' => 'Published',
    ])
    ->default('draft')
    ->disablePlaceholderSelection()

Multi-select

The multiple() method on the Select component allows you to select multiple values from the list of options:

php
use Filament\Forms\Components\Select;

Select::make('technologies')
    ->multiple()
    ->options([
        'tailwind' => 'Tailwind CSS',
        'alpine' => 'Alpine.js',
        'laravel' => 'Laravel',
        'livewire' => 'Laravel Livewire',
    ])

These options are returned in JSON format. If you're saving them using Eloquent, you should be sure to add an array cast to the model property:

php
use Illuminate\Database\Eloquent\Model;

class App extends Model
{
    protected $casts = [
        'technologies' => 'array',
    ];

    // ...
}

Instead of getOptionLabelUsing(), the getOptionLabelsUsing() method can be used to transform the selected options' $values into labels.

Dependant selects

Commonly, you may desire "dependant" select inputs, which populate their options based on the state of another.

Some of the techniques described in the advanced forms section are required to create dependant selects. These techniques can be applied across all form components for many dynamic customization possibilities.

Populating automatically from a relationship

You may employ the relationship() method of the Select to configure a BelongsTo relationship to automatically retrieve and save options from:

php
use Filament\Forms\Components\Select;

Select::make('authorId')
    ->relationship('author', 'name')

The multiple() method may be used in combination with relationship() to automatically populate from a BelongsToMany relationship:

php
use Filament\Forms\Components\Select;

Select::make('technologies')
    ->multiple()
    ->relationship('technologies', 'name')

To set this functionality up, you must also follow the instructions set out in the field relationships section. If you're using the admin panel, you can skip this step.

If you'd like to populate the options from the database when the page is loaded, instead of when the user searches, you can use the preload() method:

php
use Filament\Forms\Components\Select;

Select::make('authorId')
    ->relationship('author', 'name')
    ->preload()

You may customize the database query that retrieves options using the third parameter of the relationship() method:

php
use Filament\Forms\Components\Select;
use Illuminate\Database\Eloquent\Builder;

Select::make('authorId')
    ->relationship('author', 'name', fn (Builder $query) => $query->withTrashed())

If you'd like to customize the label of each option, maybe to be more descriptive, or to concatenate a first and last name, you should use a virtual column in your database migration:

php
$table->string('full_name')->virtualAs('concat(first_name, \' \', last_name)');
php
use Filament\Forms\Components\Select;

Select::make('authorId')
    ->relationship('author', 'full_name')

Alternatively, you can use the getOptionLabelFromRecordUsing() method to transform the selected option's Eloquent model into a label. But please note, this is much less performant than using a virtual column:

php
use Filament\Forms\Components\Select;
use Illuminate\Database\Eloquent\Model;

Select::make('authorId')
    ->relationship('author', 'first_name')
    ->getOptionLabelFromRecordUsing(fn (Model $record) => "{$record->first_name} {$record->last_name}")

Handling `MorphTo` relationships

MorphTo relationships are special, since they give the user the ability to select records from a range of different models. Because of this, we have a dedicated MorphToSelect component which is not actually a select field, rather 2 select fields inside a fieldset. The first select field allows you to select the type, and the second allows you to select the record of that type.

To use the MorphToSelect, you must pass types() into the component, which tell it how to render options for different types:

php
use Filament\Forms\Components\MorphToSelect;

MorphToSelect::make('commentable')
    ->types([
        MorphToSelect\Type::make(Product::class)->titleColumnName('name'),
        MorphToSelect\Type::make(Post::class)->titleColumnName('title'),
    ])

The titleColumnName() is used to extract the titles out of each product or post. You can choose to extract the option labels using getOptionLabelFromRecordUsing instead if you wish:

php
use Filament\Forms\Components\MorphToSelect;

MorphToSelect::make('commentable')
    ->types([
        MorphToSelect\Type::make(Product::class)
            ->getOptionLabelFromRecordUsing(fn (Product $record): string => "{$record->name} - {$record->slug}"),
        MorphToSelect\Type::make(Post::class)->titleColumnName('title'),
    ])

You may customize the database query that retrieves options using the modifyOptionsQueryUsing() method:

php
use Filament\Forms\Components\MorphToSelect;
use Illuminate\Database\Eloquent\Builder;

MorphToSelect::make('commentable')
    ->types([
        MorphToSelect\Type::make(Product::class)
            ->titleColumnName('name')
            ->modifyOptionsQueryUsing(fn (Builder $query) => $query->whereBelongsTo($this->team)),
        MorphToSelect\Type::make(Post::class)
            ->titleColumnName('title')
            ->modifyOptionsQueryUsing(fn (Builder $query) => $query->whereBelongsTo($this->team)),
    ])

Many of the same options in the select field are available for MorphToSelect, including searchable(), preload(), allowHtml(), and optionsLimit().

Creating new records

You may define a custom form that can be used to create a new record and attach it to the BelongsTo relationship:

php
use Filament\Forms\Components\Select;
use Illuminate\Database\Eloquent\Model;

Select::make('authorId')
    ->relationship('author', 'name')
    ->createOptionForm([
        Forms\Components\TextInput::make('name')
            ->required(),
        Forms\Components\TextInput::make('email')
            ->required()
            ->email(),
    ]),

The form opens in a modal, where the user can fill it with data. Upon form submission, the new record is selected by the field.

Since HTML does not support nested <form> elements, you must also render the modal outside the <form> in the view. If you're using the admin panel, this is included already:

blade
<form wire:submit.prevent="submit">
    {{ $this->form }}

    
        Submit
    
</form>

{{ $this->modal }}

Checkbox

The checkbox component, similar to a toggle, allows you to interact a boolean value.

php
use Filament\Forms\Components\Checkbox;

Checkbox::make('is_admin')

Checkbox fields have two layout modes, inline and stacked. By default, they are inline.

When the checkbox is inline, its label is adjacent to it:

php
use Filament\Forms\Components\Checkbox;

Checkbox::make('is_admin')->inline()

When the checkbox is stacked, its label is above it:

php
use Filament\Forms\Components\Checkbox;

Checkbox::make('is_admin')->inline(false)

If you're saving the boolean value using Eloquent, you should be sure to add a boolean cast to the model property:

php
use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    protected $casts = [
        'is_admin' => 'boolean',
    ];

    // ...
}

Toggle

The toggle component, similar to a checkbox, allows you to interact a boolean value.

php
use Filament\Forms\Components\Toggle;

Toggle::make('is_admin')

Toggle fields have two layout modes, inline and stacked. By default, they are inline.

When the toggle is inline, its label is adjacent to it:

php
use Filament\Forms\Components\Toggle;

Toggle::make('is_admin')->inline()

When the toggle is stacked, its label is above it:

php
use Filament\Forms\Components\Toggle;

Toggle::make('is_admin')->inline(false)

Toggles may also use an "on icon" and an "off icon". These are displayed on its handle and could provide a greater indication to what your field represents. The parameter to each method must contain the name of a Blade icon component:

php
use Filament\Forms\Components\Toggle;

Toggle::make('is_admin')
    ->onIcon('heroicon-s-lightning-bolt')
    ->offIcon('heroicon-s-user')

You may also customize the color representing each state. These may be either primary, secondary, success, warning or danger:

php
use Filament\Forms\Components\Toggle;

Toggle::make('is_admin')
    ->onColor('success')
    ->offColor('danger')

If you're saving the boolean value using Eloquent, you should be sure to add a boolean cast to the model property:

php
use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    protected $casts = [
        'is_admin' => 'boolean',
    ];

    // ...
}

Checkbox list

The checkbox list component allows you to select multiple values from a list of predefined options:

php
use Filament\Forms\Components\CheckboxList;

CheckboxList::make('technologies')
    ->options([
        'tailwind' => 'Tailwind CSS',
        'alpine' => 'Alpine.js',
        'laravel' => 'Laravel',
        'livewire' => 'Laravel Livewire',
    ])

These options are returned in JSON format. If you're saving them using Eloquent, you should be sure to add an array cast to the model property:

php
use Illuminate\Database\Eloquent\Model;

class App extends Model
{
    protected $casts = [
        'technologies' => 'array',
    ];

    // ...
}

You may organize options into columns by using the columns() method:

php
use Filament\Forms\Components\CheckboxList;

CheckboxList::make('technologies')
    ->options([
        'tailwind' => 'Tailwind CSS',
        'alpine' => 'Alpine.js',
        'laravel' => 'Laravel',
        'livewire' => 'Laravel Livewire',
    ])
    ->columns(2)

This method accepts the same options as the columns() method of the grid. This allows you to responsively customize the number of columns at various breakpoints.

You may also allow users to toggle all checkboxes at once using the bulkToggleable() method:

php
use Filament\Forms\Components\CheckboxList;

CheckboxList::make('technologies')
    ->options([
        'tailwind' => 'Tailwind CSS',
        'alpine' => 'Alpine.js',
        'laravel' => 'Laravel',
        'livewire' => 'Laravel Livewire',
    ])
    ->bulkToggleable()

Populating automatically from a relationship

You may employ the relationship() method to configure a relationship to automatically retrieve and save options from:

php
use Filament\Forms\Components\CheckboxList;

CheckboxList::make('technologies')
    ->relationship('technologies', 'name')

To set this functionality up, you must also follow the instructions set out in the field relationships section. If you're using the admin panel, you can skip this step.

You may customize the database query that retrieves options using the third parameter of the relationship() method:

php
use Filament\Forms\Components\CheckboxList;
use Illuminate\Database\Eloquent\Builder;

CheckboxList::make('technologies')
    ->relationship('technologies', 'name', fn (Builder $query) => $query->withTrashed())

If you'd like to customize the label of each option, maybe to be more descriptive, or to concatenate a first and last name, you should use a virtual column in your database migration:

php
$table->string('full_name')->virtualAs('concat(first_name, \' \', last_name)');
php
use Filament\Forms\Components\CheckboxList;

CheckboxList::make('participants')
    ->relationship('participants', 'full_name')

Alternatively, you can use the getOptionLabelFromRecordUsing() method to transform the selected option's Eloquent model into a label. But please note, this is much less performant than using a virtual column:

php
use Filament\Forms\Components\CheckboxList;
use Illuminate\Database\Eloquent\Model;

CheckboxList::make('participants')
    ->relationship('participants', 'first_name')
    ->getOptionLabelFromRecordUsing(fn (Model $record) => "{$record->first_name} {$record->last_name}")

Radio

The radio input provides a radio button group for selecting a single value from a list of predefined options:

php
use Filament\Forms\Components\Radio;

Radio::make('status')
    ->options([
        'draft' => 'Draft',
        'scheduled' => 'Scheduled',
        'published' => 'Published'
    ])

You can optionally provide descriptions to each option using the descriptions() method:

php
use Filament\Forms\Components\Radio;

Radio::make('status')
    ->options([
        'draft' => 'Draft',
        'scheduled' => 'Scheduled',
        'published' => 'Published'
    ])
    ->descriptions([
        'draft' => 'Is not visible.',
        'scheduled' => 'Will be visible.',
        'published' => 'Is visible.'
    ])

Be sure to use the same key in the descriptions array as the key in the options array so the right description matches the right option.

If you want a simple boolean radio button group, with "Yes" and "No" options, you can use the boolean() method:

php
Radio::make('feedback')
    ->label('Do you like this post?')
    ->boolean()

You may wish to display the options inline() with the label:

php
Radio::make('feedback')
    ->label('Do you like this post?')
    ->boolean()
    ->inline()

Date-time picker

The date-time picker provides an interactive interface for selecting a date and a time.

php
use Filament\Forms\Components\DatePicker;
use Filament\Forms\Components\DateTimePicker;
use Filament\Forms\Components\TimePicker;

DateTimePicker::make('published_at')
DatePicker::make('date_of_birth')
TimePicker::make('alarm_at')

You may restrict the minimum and maximum date that can be selected with the picker. The minDate() and maxDate() methods accept a DateTime instance (e.g. Carbon), or a string:

php
use Filament\Forms\Components\DatePicker;

DatePicker::make('date_of_birth')
    ->minDate(now()->subYears(150))
    ->maxDate(now())

You may customize the format of the field when it is saved in your database, using the format() method. This accepts a string date format, using PHP date formatting tokens:

php
use Filament\Forms\Components\DatePicker;

DatePicker::make('date_of_birth')->format('d/m/Y')

You may also customize the display format of the field, separately from the format used when it is saved in your database. For this, use the displayFormat() method, which also accepts a string date format, using PHP date formatting tokens:

php
use Filament\Forms\Components\DatePicker;

DatePicker::make('date_of_birth')->displayFormat('d/m/Y')

When using the time picker, you may disable the seconds input using the withoutSeconds() method:

php
use Filament\Forms\Components\DateTimePicker;

DateTimePicker::make('published_at')->withoutSeconds()

You may also customize the input interval for increasing the hours / minutes / seconds using the hoursStep() , minutesStep() or secondsStep() methods:

php
use Filament\Forms\Components\DateTimePicker;

DateTimePicker::make('published_at')
    ->hoursStep(2)
    ->minutesStep(15)
    ->secondsStep(10)

In some countries, the first day of the week is not Monday. To customize the first day of the week in the date picker, use the forms.components.date_time_picker.first_day_of_week config option, or the firstDayOfWeek() method on the component. 0 to 7 are accepted values, with Monday as 1 and Sunday as 7 or 0:

php
use Filament\Forms\Components\DateTimePicker;

DateTimePicker::make('published_at')->firstDayOfWeek(7)

There are additionally convenient helper methods to set the first day of the week more semantically:

php
use Filament\Forms\Components\DateTimePicker;

DateTimePicker::make('published_at')->weekStartsOnMonday()
DateTimePicker::make('published_at')->weekStartsOnSunday()

To disable specific dates:

php
use Filament\Forms\Components\DateTimePicker;

DateTimePicker::make('date')
    ->label('Appointment date')
    ->minDate(now())
    ->maxDate(Carbon::now()->addDays(30))
    ->disabledDates(['2022-10-02', '2022-10-05', '2022-10-15'])

You may change the calendar icon using the icon() method:

php
use Filament\Forms\Components\DateTimePicker;

DateTimePicker::make('date')
    ->icon('heroicon-o-calendar')

Alternatively, you may remove the icon altogether by passing false:

php
use Filament\Forms\Components\DateTimePicker;

DateTimePicker::make('date')
    ->icon(false)

Timezones

If you'd like users to be able to manage dates in their own timezone, you can use the timezone() method:

php
use Filament\Forms\Components\DateTimePicker;

DateTimePicker::make('published_at')->timezone('America/New_York')

While dates will still be stored using the app's configured timezone, the date will now load in the new timezone, and it will be converted back when the form is saved.

File upload

The file upload field is based on Filepond.

php
use Filament\Forms\Components\FileUpload;

FileUpload::make('attachment')

By default, files will be uploaded publicly to your default storage disk.

Please note, to correctly preview images and other files, FilePond requires files to be served from the same domain as the app, or the appropriate CORS headers need to be present. Ensure that the APP_URL environment variable is correct, or modify the filesystem driver to set the correct URL. If you're hosting files on a separate domain like S3, ensure that CORS headers are set up.

To change the disk and directory that files are saved in, and their visibility, use the disk(), directory() and visibility methods:

php
use Filament\Forms\Components\FileUpload;

FileUpload::make('attachment')
    ->disk('s3')
    ->directory('form-attachments')
    ->visibility('private')

Please note, it is the responsibility of the developer to delete these files from the disk if they are removed, as Filament is unaware if they are depended on elsewhere. One way to do this automatically is observing a model event.

By default, a random file name will be generated for newly-uploaded files. To instead preserve the original filenames of the uploaded files, use the preserveFilenames() method:

php
use Filament\Forms\Components\FileUpload;

FileUpload::make('attachment')->preserveFilenames()

You may completely customize how file names are generated using the getUploadedFileNameForStorageUsing() method, and returning a string from the callback:

php
use Livewire\TemporaryUploadedFile;

FileUpload::make('attachment')
    ->getUploadedFileNameForStorageUsing(function (TemporaryUploadedFile $file): string {
        return (string) str($file->getClientOriginalName())->prepend('custom-prefix-');
    })

You can keep the randomly generated file names, while still storing the original file name, using the storeFileNamesIn() method:

php
use Filament\Forms\Components\FileUpload;

FileUpload::make('attachments')
    ->multiple()
    ->storeFileNamesIn('attachment_file_names')

attachment_file_names will now store the original file name/s of your uploaded files.

You may restrict the types of files that may be uploaded using the acceptedFileTypes() method, and passing an array of MIME types. You may also use the image() method as shorthand to allow all image MIME types.

php
use Filament\Forms\Components\FileUpload;

FileUpload::make('document')->acceptedFileTypes(['application/pdf'])
FileUpload::make('image')->image()

You may also restrict the size of uploaded files, in kilobytes:

php
use Filament\Forms\Components\FileUpload;

FileUpload::make('attachment')
    ->minSize(512)
    ->maxSize(1024)

To customize Livewire's default file upload validation rules, including the 12MB file size maximum, please refer to its documentation.

Filepond allows you to crop and resize images before they are uploaded. You can customize this behaviour using the imageResizeMode(), imageCropAspectRatio(), imageResizeTargetHeight() and imageResizeTargetWidth() methods. imageResizeMode() should be set for the other methods to have an effect - either force, cover, or contain.

php
use Filament\Forms\Components\FileUpload;

FileUpload::make('image')
    ->image()
    ->imageResizeMode('cover')
    ->imageCropAspectRatio('16:9')
    ->imageResizeTargetWidth('1920')
    ->imageResizeTargetHeight('1080')

You may also alter the general appearance of the Filepond component. Available options for these methods are available on the Filepond website.

php
use Filament\Forms\Components\FileUpload;

FileUpload::make('attachment')
    ->imagePreviewHeight('250')
    ->loadingIndicatorPosition('left')
    ->panelAspectRatio('2:1')
    ->panelLayout('integrated')
    ->removeUploadedFileButtonPosition('right')
    ->uploadButtonPosition('left')
    ->uploadProgressIndicatorPosition('left')

You may also upload multiple files. This stores URLs in JSON:

php
use Filament\Forms\Components\FileUpload;

FileUpload::make('attachments')->multiple()

If you're saving the file URLs using Eloquent, you should be sure to add an array cast to the model property:

php
use Illuminate\Database\Eloquent\Model;

class Message extends Model
{
    protected $casts = [
        'attachments' => 'array',
    ];

    // ...
}

You may customize the number of files that may be uploaded, using the minFiles() and maxFiles() methods:

php
use Filament\Forms\Components\FileUpload;

FileUpload::make('attachments')
    ->multiple()
    ->minFiles(2)
    ->maxFiles(5)

You can also enable the re-ordering of uploaded files using the enableReordering() method:

php
use Filament\Forms\Components\FileUpload;

FileUpload::make('attachments')
    ->multiple()
    ->enableReordering()

You can add a button to open each file in a new tab with the enableOpen() method:

php
use Filament\Forms\Components\FileUpload;

FileUpload::make('attachments')
    ->multiple()
    ->enableOpen()

If you wish to add a download button to each file instead, you can use the enableDownload() method:

php
use Filament\Forms\Components\FileUpload;

FileUpload::make('attachments')
    ->multiple()
    ->enableDownload()

Filament also supports spatie/laravel-medialibrary. See our plugin documentation for more information.

Rich editor

The rich editor allows you to edit and preview HTML content, as well as upload images.

php
use Filament\Forms\Components\RichEditor;

RichEditor::make('content')

You may enable / disable toolbar buttons using a range of convenient methods:

php
use Filament\Forms\Components\RichEditor;

RichEditor::make('content')
    ->toolbarButtons([
        'attachFiles',
        'blockquote',
        'bold',
        'bulletList',
        'codeBlock',
        'h2',
        'h3',
        'italic',
        'link',
        'orderedList',
        'redo',
        'strike',
        'underline',
        'undo',
    ])
RichEditor::make('content')
    ->disableToolbarButtons([
        'attachFiles',
        'codeBlock',
    ])
RichEditor::make('content')
    ->disableAllToolbarButtons()
    ->enableToolbarButtons([
        'bold',
        'bulletList',
        'italic',
        'strike',
    ])

You may customize how images are uploaded using configuration methods:

php
use Filament\Forms\Components\RichEditor;

RichEditor::make('content')
    ->fileAttachmentsDisk('s3')
    ->fileAttachmentsDirectory('attachments')
    ->fileAttachmentsVisibility('private')

Markdown editor

The markdown editor allows you to edit and preview markdown content, as well as upload images using drag and drop.

php
use Filament\Forms\Components\MarkdownEditor;

MarkdownEditor::make('content')

You may enable / disable toolbar buttons using a range of convenient methods:

php
use Filament\Forms\Components\MarkdownEditor;

MarkdownEditor::make('content')
    ->toolbarButtons([
        'attachFiles',
        'bold',
        'bulletList',
        'codeBlock',
        'edit',
        'italic',
        'link',
        'orderedList',
        'preview',
        'strike',
    ])
MarkdownEditor::make('content')
    ->disableToolbarButtons([
        'attachFiles',
        'codeBlock',
    ])
MarkdownEditor::make('content')
    ->disableAllToolbarButtons()
    ->enableToolbarButtons([
        'bold',
        'bulletList',
        'edit',
        'italic',
        'preview',
        'strike',
    ])

You may customize how images are uploaded using configuration methods:

php
use Filament\Forms\Components\MarkdownEditor;

MarkdownEditor::make('content')
    ->fileAttachmentsDisk('s3')
    ->fileAttachmentsDirectory('attachments')
    ->fileAttachmentsVisibility('private')

Hidden

The hidden component allows you to create a hidden field in your form that holds a value.

php
use Filament\Forms\Components\Hidden;

Hidden::make('token')

Repeater

The repeater component allows you to output a JSON array of repeated form components.

php
use Filament\Forms\Components\Repeater;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;

Repeater::make('members')
    ->schema([
        TextInput::make('name')->required(),
        Select::make('role')
            ->options([
                'member' => 'Member',
                'administrator' => 'Administrator',
                'owner' => 'Owner',
            ])
            ->required(),
    ])
    ->columns(2)

We recommend that you store repeater data with a JSON column in your database. Additionally, if you're using Eloquent, make sure that column has an array cast.

As evident in the above example, the component schema can be defined within the schema() method of the component:

php
use Filament\Forms\Components\Repeater;
use Filament\Forms\Components\TextInput;

Repeater::make('members')
    ->schema([
        TextInput::make('name')->required(),
        // ...
    ])

If you wish to define a repeater with multiple schema blocks that can be repeated in any order, please use the builder.

Repeaters may have a certain number of empty items created by default, using the defaultItems() method:

php
use Filament\Forms\Components\Repeater;

Repeater::make('members')
    ->schema([
        // ...
    ])
    ->defaultItems(3)

You may set a label to customize the text that should be displayed in the button for adding a repeater item:

php
use Filament\Forms\Components\Repeater;

Repeater::make('members')
    ->schema([
        // ...
    ])
    ->createItemButtonLabel('Add member')

You may also prevent the user from adding items, deleting items, or moving items inside the repeater:

php
use Filament\Forms\Components\Repeater;

Repeater::make('members')
    ->schema([
        // ...
    ])
    ->disableItemCreation()
    ->disableItemDeletion()
    ->disableItemMovement()

You may customize the number of items that may be created, using the minItems() and maxItems() methods:

php
use Filament\Forms\Components\Repeater;

Repeater::make('members')
    ->schema([
        // ...
    ])
    ->minItems(1)
    ->maxItems(10)

Collapsible

The repeater may be collapsible() to optionally hide content in long forms:

php
use Filament\Forms\Components\Repeater;

Repeater::make('qualifications')
    ->schema([
        // ...
    ])
    ->collapsible()

You may collapse all items by default:

php
use Filament\Forms\Components\Repeater;

Repeater::make('qualifications')
    ->schema([
        // ...
    ])
    ->collapsed()

Cloning items

You may allow repeater items to be duplicated using the cloneable() method:

php
use Filament\Forms\Components\Repeater;

Repeater::make('qualifications')
    ->schema([
        // ...
    ])
    ->cloneable()

Populating automatically from a relationship

You may employ the relationship() method of the repeater to configure a relationship to automatically retrieve and save repeater items:

php
use Filament\Forms\Components\Repeater;

Repeater::make('qualifications')
    ->relationship()
    ->schema([
        // ...
    ])

To set this functionality up, you must also follow the instructions set out in the field relationships section. If you're using the admin panel, you can skip this step.

Ordering items

By default, ordering relationship repeater items is disabled. This is because your related model needs an sort column to store the order of related records. To enable ordering, you may use the orderable() method:

php
use Filament\Forms\Components\Repeater;

Repeater::make('qualifications')
    ->relationship()
    ->schema([
        // ...
    ])
    ->orderable()

This assumes that your related model has a sort column.

If you use something like spatie/eloquent-sortable with an order column such as order_column, you may pass this in to orderable():

php
use Filament\Forms\Components\Repeater;

Repeater::make('qualifications')
    ->relationship()
    ->schema([
        // ...
    ])
    ->orderable('order_column')

Grid layout

You may organize repeater items into columns by using the grid() method:

php
use Filament\Forms\Components\Repeater;

Repeater::make('members')
    ->schema([
        // ...
    ])
    ->grid(2)

This method accepts the same options as the columns() method of the grid. This allows you to responsively customize the number of grid columns at various breakpoints.

Item labels

You may add a label for repeater items using the itemLabel() method:

php
use Filament\Forms\Components\Repeater;
use Filament\Forms\Components\TextInput;

Repeater::make('members')
    ->schema([
        TextInput::make('name')
            ->lazy(),
    ])
    ->itemLabel(fn (array $state): ?string => $state['name'] ?? null),

Any fields that you use from $state should be reactive() or lazy() if you wish to see the item label update live as you use the form.

Using `$get()` to access parent field values

All form components are able to use $get() and $set() to access another field's value. However, you might experience unexpected behaviour when using this inside the repeater's schema.

This is because $get() and $set(), by default, are scoped to the current repeater item. This means that you are able to interact with another field inside that repeater item easily without knowing which repeater item the current form component belongs to.

The consequence of this, is that you may be confused when you are unable to interact with a field outside the repeater. We use ../ syntax to solve this problem - $get('../../parent_field_name').

Consider your form has this data structure:

php
[
    'client_id' => 1,

    'repeater' => [
        'item1' => [
            'service_id' => 2,
        ],
    ],
]

You are trying to retrieve the value of client_id from inside the repeater item.

$get() is relative to the current repeater item, so $get('client_id') is looking for $get('repeater.item1.client_id').

You can use ../ to go up a level in the data structure, so $get('../client_id') is $get('repeater.client_id') and $get('../../client_id') is $get('client_id').

Builder

Similar to a repeater, the builder component allows you to output a JSON array of repeated form components. Unlike the repeater, which only defines one form schema to repeat, the builder allows you to define different schema "blocks", which you can repeat in any order. This makes it useful for building more advanced array structures.

The primary use of the builder component is to build web page content using predefined blocks. The example below defines multiple blocks for different elements in the page content. On the frontend of your website, you could loop through each block in the JSON and format it how you wish.

php
use Filament\Forms\Components\Builder;
use Filament\Forms\Components\FileUpload;
use Filament\Forms\Components\MarkdownEditor;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;

Builder::make('content')
    ->blocks([
        Builder\Block::make('heading')
            ->schema([
                TextInput::make('content')
                    ->label('Heading')
                    ->required(),
                Select::make('level')
                    ->options([
                        'h1' => 'Heading 1',
                        'h2' => 'Heading 2',
                        'h3' => 'Heading 3',
                        'h4' => 'Heading 4',
                        'h5' => 'Heading 5',
                        'h6' => 'Heading 6',
                    ])
                    ->required(),
            ]),
        Builder\Block::make('paragraph')
            ->schema([
                MarkdownEditor::make('content')
                    ->label('Paragraph')
                    ->required(),
            ]),
        Builder\Block::make('image')
            ->schema([
                FileUpload::make('url')
                    ->label('Image')
                    ->image()
                    ->required(),
                TextInput::make('alt')
                    ->label('Alt text')
                    ->required(),
            ]),
    ])

We recommend that you store builder data with a JSON column in your database. Additionally, if you're using Eloquent, make sure that column has an array cast.

As evident in the above example, blocks can be defined within the blocks() method of the component. Blocks are Builder\Block objects, and require a unique name, and a component schema:

php
use Filament\Forms\Components\Builder;
use Filament\Forms\Components\TextInput;

Builder::make('content')
    ->blocks([
        Builder\Block::make('heading')
            ->schema([
                TextInput::make('content')->required(),
                // ...
            ]),
        // ...
    ])

By default, the label of the block will be automatically determined based on its name. To override the block's label, you may use the label() method. Customizing the label in this way is useful if you wish to use a translation string for localization:

php
use Filament\Forms\Components\Builder;

Builder\Block::make('heading')->label(__('blocks.heading'))

Blocks may also have an icon, which is displayed next to the label. The icon() method accepts the name of any Blade icon component:

php
use Filament\Forms\Components\Builder;

Builder\Block::make('heading')->icon('heroicon-o-bookmark')

You may customize the number of items that may be created, using the minItems() and maxItems() methods:

php
use Filament\Forms\Components\Builder;
use Filament\Forms\Components\TextInput;

Builder::make('content')
    ->blocks([
        // ...
    ])
    ->minItems(1)
    ->maxItems(10)

Collapsible

The builder may be collapsible() to optionally hide content in long forms:

php
use Filament\Forms\Components\Builder;

Builder::make('content')
    ->blocks([
        // ...
    ])
    ->collapsible()

You may collapse all items by default:

php
use Filament\Forms\Components\Builder;

Builder::make('content')
    ->blocks([
        // ...
    ])
    ->collapsed()

Tags input

The tags input component allows you to interact with a list of tags.

By default, tags are stored in JSON:

php
use Filament\Forms\Components\TagsInput;

TagsInput::make('tags')

If you're saving the JSON tags using Eloquent, you should be sure to add an array cast to the model property:

php
use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    protected $casts = [
        'tags' => 'array',
    ];

    // ...
}

You may allow the tags to be stored in a separated string, instead of JSON. To set this up, pass the separating character to the separator() method:

php
use Filament\Forms\Components\TagsInput;

TagsInput::make('tags')->separator(',')

Tags inputs may have autocomplete suggestions. To enable this, pass an array of suggestions to the suggestions() method:

php
use Filament\Forms\Components\TagsInput;

TagsInput::make('tags')
    ->suggestions([
        'tailwindcss',
        'alpinejs',
        'laravel',
        'livewire',
    ])

Filament also supports spatie/laravel-tags. See our plugin documentation for more information.

Textarea

The textarea allows you to interact with a multi-line string:

php
use Filament\Forms\Components\Textarea;

Textarea::make('description')

You may change the size of the textarea by defining the rows() and cols() methods:

php
use Filament\Forms\Components\Textarea;

Textarea::make('description')
    ->rows(10)
    ->cols(20)

You may limit the length of the string by setting the minLength() and maxLength() methods. These methods add both frontend and backend validation:

php
use Filament\Forms\Components\Textarea;

Textarea::make('description')
    ->minLength(50)
    ->maxLength(500)

Key-value

The key-value field allows you to interact with one-dimensional JSON object:

php
use Filament\Forms\Components\KeyValue;

KeyValue::make('meta')

You may customize the labels for the key and value fields using the keyLabel() and valueLabel() methods:

php
use Filament\Forms\Components\KeyValue;

KeyValue::make('meta')
    ->keyLabel('Property name')
    ->valueLabel('Property value')

You may also prevent the user from adding rows, deleting rows, or editing keys:

php
use Filament\Forms\Components\KeyValue;

KeyValue::make('meta')
    ->disableAddingRows()
    ->disableDeletingRows()
    ->disableEditingKeys()

You can allow the user to reorder rows within the table:

php
use Filament\Forms\Components\KeyValue;

KeyValue::make('meta')
    ->reorderable()

You may also add placeholders for the key and value fields using the keyPlaceholder() and valuePlaceholder() methods:

php
use Filament\Forms\Components\KeyValue;

KeyValue::make('meta')
    ->keyPlaceholder('Property name')
    ->valuePlaceholder('Property value')

Color picker

The color picker component allows you to pick a color in a range of formats.

By default, the component uses HEX format:

php
use Filament\Forms\Components\ColorPicker;

ColorPicker::make('color')

Alternatively, you can use a different format:

php
use Filament\Forms\Components\ColorPicker;

ColorPicker::make('hsl_color')->hsl()
ColorPicker::make('rgb_color')->rgb()
ColorPicker::make('rgba_color')->rgba()

View

Aside from building custom fields, you may create "view" fields which allow you to create custom fields without extra PHP classes.

php
use Filament\Forms\Components\ViewField;

ViewField::make('notifications')->view('filament.forms.components.range-slider')

Inside your view, you may interact with the state of the form component using Livewire and Alpine.js.

The $getStatePath() closure may be used by the view to retrieve the Livewire property path of the field. You could use this to wire:model a value, or $wire.entangle it with Alpine.js.

Using Livewire's entangle allows sharing state with Alpine.js:

blade
<x-dynamic-component
    :component="$getFieldWrapperView()"
    :id="$getId()"
    :label="$getLabel()"
    :label-sr-only="$isLabelHidden()"
    :helper-text="$getHelperText()"
    :hint="$getHint()"
    :hint-action="$getHintAction()"
    :hint-color="$getHintColor()"
    :hint-icon="$getHintIcon()"
    :required="$isRequired()"
    :state-path="$getStatePath()"
>
    
        
    
</x-dynamic-component>

Or, you may bind the value to a Livewire property using wire:model:

blade
<x-dynamic-component
    :component="$getFieldWrapperView()"
    :id="$getId()"
    :label="$getLabel()"
    :label-sr-only="$isLabelHidden()"
    :helper-text="$getHelperText()"
    :hint="$getHint()"
    :hint-action="$getHintAction()"
    :hint-color="$getHintColor()"
    :hint-icon="$getHintIcon()"
    :required="$isRequired()"
    :state-path="$getStatePath()"
>
    
</x-dynamic-component>

Building custom fields

You may create your own custom field classes and views, which you can reuse across your project, and even release as a plugin to the community.

If you're just creating a simple custom field to use once, you could instead use a view field to render any custom Blade file.

To create a custom field class and view, you may use the following command:

bash
php artisan make:form-field RangeSlider

This will create the following field class:

php
use Filament\Forms\Components\Field;

class RangeSlider extends Field
{
    protected string $view = 'filament.forms.components.range-slider';
}

Inside your view, you may interact with the state of the form component using Livewire and Alpine.js.

The $getStatePath() closure may be used by the view to retrieve the Livewire property path of the field. You could use this to wire:model a value, or $wire.entangle it with Alpine.js:

blade
<x-dynamic-component
    :component="$getFieldWrapperView()"
    :id="$getId()"
    :label="$getLabel()"
    :label-sr-only="$isLabelHidden()"
    :helper-text="$getHelperText()"
    :hint="$getHint()"
    :hint-action="$getHintAction()"
    :hint-color="$getHintColor()"
    :hint-icon="$getHintIcon()"
    :required="$isRequired()"
    :state-path="$getStatePath()"
>
    
        
    
</x-dynamic-component>

File: docs/04-layout.md


title: Layout

Getting started

Layout component classes can be found in the Filament\Form\Components namespace.

They reside within the schema of your form, alongside any fields.

If you're using the layout components in a Livewire component, you can put them in the getFormSchema() method:

php
protected function getFormSchema(): array
{
    return [
        // ...
    ];
}

If you're using them in admin panel resources or relation managers, you must put them in the $form->schema() method:

php
public static function form(Form $form): Form
{
    return $form
        ->schema([
            // ...
        ]);
}

Components may be created using the static make() method. Usually, you will then define the child component schema() to display inside:

php
use Filament\Forms\Components\Grid;

Grid::make()
    ->schema([
        // ...
    ])

Columns

You may create multiple columns within each layout component using the columns() method:

php
use Filament\Forms\Components\Card;

Card::make()->columns(2)

For more information about creating advanced, responsive column layouts, please see the grid section. All column options in that section are also available in other layout components.

Controlling field column span

You may specify the number of columns that any component may span in the parent grid:

php
use Filament\Forms\Components\Grid;
use Filament\Forms\Components\RichEditor;
use Filament\Forms\Components\TextInput;

Grid::make(3)
    ->schema([
        TextInput::make('name')
            ->columnSpan(2),
        // ...
    ])

You may use columnSpan('full') to ensure that a column spans the full width of the parent grid, however many columns it has:

php
use Filament\Forms\Components\Grid;
use Filament\Forms\Components\RichEditor;
use Filament\Forms\Components\TextInput;

Grid::make(3)
    ->schema([
        TextInput::make('name')
            ->columnSpan('full'),
        // ...
    ])

Instead, you can even define how many columns a component may consume at any breakpoint:

php
use Filament\Forms\Components\Grid;
use Filament\Forms\Components\TextInput;

Grid::make([
    'default' => 1,
    'sm' => 3,
    'xl' => 6,
    '2xl' => 8,
])
    ->schema([
        TextInput::make('name')
            ->columnSpan([
                'sm' => 2,
                'xl' => 3,
                '2xl' => 4,
            ]),
        // ...
    ])

Setting an ID

You may define an ID for the component using the id() method:

php
use Filament\Forms\Components\Card;

Card::make()->id('main-card')

Custom attributes

The HTML of components can be customized even further, by passing an array of extraAttributes():

php
use Filament\Forms\Components\Card;

Card::make()->extraAttributes(['class' => 'bg-gray-50'])

Global settings

If you wish to change the default behaviour of a component globally, then you can call the static configureUsing() method inside a service provider's boot() method, to which you pass a Closure to modify the component using. For example, if you wish to make all card components have 2 columns by default, you can do it like so:

php
use Filament\Forms\Components\Card;

Card::configureUsing(function (Card $card): void {
    $card->columns(2);
});

Of course, you are still able to overwrite this on each field individually:

php
use Filament\Forms\Components\Card;

Card::make()->columns(1)

Saving data to relationships

You may load and save the contents of a layout component to a HasOne, BelongsTo or MorphOne Eloquent relationship, using the relationship() method:

php
use Filament\Forms\Components\Fieldset;
use Filament\Forms\Components\FileUpload;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput;

Fieldset::make('Metadata')
    ->relationship('metadata')
    ->schema([
        TextInput::make('title'),
        Textarea::make('description'),
        FileUpload::make('image'),
    ])

In this example, the title, description and image are automatically loaded from the metadata relationship, and saved again when the form is submitted. If the metadata record does not exist, it is automatically created.

To set this functionality up, you must also follow the instructions set out in the field relationships section. If you're using the admin panel, you can skip this step.

Grid

Generally, form fields are stacked on top of each other in one column. To change this, you may use a grid component:

php
use Filament\Forms\Components\Grid;

Grid::make()
    ->schema([
        // ...
    ])

By default, grid components will create a two column grid for the Tailwind md breakpoint and higher.

You may pass a different number of columns to the grid's md breakpoint:

php
use Filament\Forms\Components\Grid;

Grid::make(3)
    ->schema([
        // ...
    ])

To customize the number of columns in any grid at different breakpoints, you may pass an array of breakpoints and columns:

php
use Filament\Forms\Components\Grid;

Grid::make([
    'default' => 1,
    'sm' => 2,
    'md' => 3,
    'lg' => 4,
    'xl' => 6,
    '2xl' => 8,
])
    ->schema([
        // ...
    ])

Since Tailwind is mobile-first, if you leave out a breakpoint, it will fall back to the one set below it:

php
use Filament\Forms\Components\Grid;

Grid::make([
    'sm' => 2,
    'xl' => 6,
])
    ->schema([
        // ...
    ])

Fieldset

You may want to group fields into a Fieldset. Each fieldset has a label, a border, and a two-column grid by default:

php
use Filament\Forms\Components\Fieldset;

Fieldset::make('Label')
    ->schema([
        // ...
    ])

You may use the columns() method to customize the grid within the fieldset:

php
use Filament\Forms\Components\Fieldset;

Fieldset::make('Label')
    ->schema([
        // ...
    ])
    ->columns(3)

Tabs

Some forms can be long and complex. You may want to use tabs to reduce the number of components that are visible at once:

php
use Filament\Forms\Components\Tabs;

Tabs::make('Heading')
    ->tabs([
        Tabs\Tab::make('Label 1')
            ->schema([
                // ...
            ]),
        Tabs\Tab::make('Label 2')
            ->schema([
                // ...
            ]),
        Tabs\Tab::make('Label 3')
            ->schema([
                // ...
            ]),
    ])

The first tab will be open by default. You can change the default open tab using the activeTab() method:

php
use Filament\Forms\Components\Tabs;

Tabs::make('Heading')
    ->tabs([
        Tabs\Tab::make('Label 1')
            ->schema([
                // ...
            ]),
        Tabs\Tab::make('Label 2')
            ->schema([
                // ...
            ]),
        Tabs\Tab::make('Label 3')
            ->schema([
                // ...
            ]),
    ])
    ->activeTab(2)

Tabs may have an icon and badge, which you can set using the icon() and badge() methods:

php
use Filament\Forms\Components\Tabs;

Tabs::make('Heading')
    ->tabs([
        Tabs\Tab::make('Notifications')
            ->icon('heroicon-o-bell')
            ->badge('39')
            ->schema([
                // ...
            ]),
        // ...
    ])

Icons can be modified using the iconPosition() and iconColor() methods:

php
use Filament\Forms\Components\Tabs;

Tabs::make('Heading')
    ->tabs([
        Tabs\Tab::make('Notifications')
            ->icon('heroicon-o-bell')
            ->iconPosition('after') // `before` or `after`
            ->iconColor('success') // `danger`, `primary`, `success`, `warning` or `secondary`
            ->schema([
                // ...
            ]),
        // ...
    ])

Wizard

Similar to tabs, you may want to use a multistep form wizard to reduce the number of components that are visible at once. These are especially useful if your form has a definite chronological order, in which you want each step to be validated as the user progresses.

php
use Filament\Forms\Components\Wizard;

Wizard::make([
    Wizard\Step::make('Order')
        ->schema([
            // ...
        ]),
    Wizard\Step::make('Delivery')
        ->schema([
            // ...
        ]),
    Wizard\Step::make('Billing')
        ->schema([
            // ...
        ]),
])

We have different setup instructions if you're looking to add a wizard to an admin panel resource Create page or a table action. Following that documentation will ensure that the ability to submit the form is only available on the last step.

Each step has a mandatory label. You may optionally also add a description for extra detail:

php
use Filament\Forms\Components\Wizard;

Wizard\Step::make('Order')
    ->description('Review your basket')
    ->schema([
        // ...
    ]),

Steps may also have an icon, which can be the name of any Blade icon component:

php
use Filament\Forms\Components\Wizard;

Wizard\Step::make('Order')
    ->icon('heroicon-o-shopping-bag')
    ->schema([
        // ...
    ]),

You may use the submitAction() method to render submit button HTML or a view at the end of the wizard, on the last step. This provides a clearer UX than displaying a submit button below the wizard at all times:

php
use Filament\Forms\Components\Wizard;
use Illuminate\Support\HtmlString;

Wizard::make([
    // ...
])->submitAction(view('order-form.submit-button'))

Wizard::make([
    // ...
])->submitAction(new HtmlString('Submit'))

You may use the startOnStep() method to load a specific step in the wizard:

php
use Filament\Forms\Components\Wizard;

Wizard::make([
    // ...
])->startOnStep(2)

If you'd like to allow free navigation, so all steps are skippable, use the skippable() method:

php
use Filament\Forms\Components\Wizard;

Wizard::make([
    // ...
])->skippable()

Section

You may want to separate your fields into sections, each with a heading and description. To do this, you can use a section component:

php
use Filament\Forms\Components\Section;

Section::make('Heading')
    ->description('Description')
    ->schema([
        // ...
    ])

You may use the columns() method to easily create a grid within the section:

php
use Filament\Forms\Components\Section;

Section::make('Heading')
    ->schema([
        // ...
    ])
    ->columns(2)

You may use the aside() to align heading & description on the left, and the form components inside a card on the right:

php
use Filament\Forms\Components\Section;

Section::make('Heading')
    ->description('Description')
    ->aside()
    ->schema([
        // ...
    ])

Sections may be collapsible() to optionally hide content in long forms:

php
use Filament\Forms\Components\Section;

Section::make('Heading')
    ->schema([
        // ...
    ])
    ->collapsible()

Your sections may be collapsed() by default:

php
use Filament\Forms\Components\Section;

Section::make('Heading')
    ->schema([
        // ...
    ])
    ->collapsed()

When nesting sections, you can use a more compact styling:

php
use Filament\Forms\Components\Section;

Section::make('Heading')
    ->schema([
        // ...
    ])
    ->compact()

Placeholder

Placeholders can be used to render text-only "fields" within your forms. Each placeholder has content(), which cannot be changed by the user.

Important: All fields require a unique name. That also applies to Placeholders!

php
use Filament\Forms\Components\Placeholder;

Placeholder::make('Label')
    ->content('Content, displayed underneath the label')

You may even render custom HTML within placeholder content:

php
use Filament\Forms\Components\Placeholder;
use Illuminate\Support\HtmlString;

Placeholder::make('Documentation')
    ->content(new HtmlString('[filamentphp.com](https://filamentphp.com/docs)'))

Card

The card component may be used to render the form components inside a card:

php
use Filament\Forms\Components\Card;

Card::make()
    ->schema([
        // ...
    ])

You may use the columns() method to easily create a grid within the card:

php
use Filament\Forms\Components\Card;

Card::make()
    ->schema([
        // ...
    ])
    ->columns(2)

Inline labels

You may use the inlineLabel() method to make the form labels and fields in separate columns, inline with each other. It works on all layout components, each field inside will have an inline label.

php
use Filament\Forms\Components\Card;

Card::make()
    ->schema([
        // ...
    ])
    ->inlineLabel()

View

Aside from building custom layout components, you may create "view" components which allow you to create custom layouts without extra PHP classes.

php
use Filament\Forms\Components\View;

View::make('filament.forms.components.wizard')

Inside your view, you may render the component's schema() using the $getChildComponentContainer() closure:

blade
{{ $getChildComponentContainer() }}

Building custom layout components

You may create your own custom component classes and views, which you can reuse across your project, and even release as a plugin to the community.

If you're just creating a simple custom component to use once, you could instead use a view component to render any custom Blade file.

To create a custom column class and view, you may use the following command:

bash
php artisan make:form-layout Wizard

This will create the following layout component class:

php
use Filament\Forms\Components\Component;

class Wizard extends Component
{
    protected string $view = 'filament.forms.components.wizard';

    public static function make(): static
    {
        return new static();
    }
}

Inside your view, you may render the component's schema() using the $getChildComponentContainer() closure:

blade
{{ $getChildComponentContainer() }}

File: docs/05-validation.md


title: Validation

Getting started

Validation rules may be added to any field.

Filament includes several dedicated validation methods, but you can also use any other Laravel validation rules, including custom validation rules.

Beware that some validations rely on the field name and therefore won't work when passed via ->rule()/->rules(). Use the dedicated validation methods whenever you can.

Available rules

Active URL

The field must have a valid A or AAAA record according to the dns_get_record() PHP function. See the Laravel documentation.

php
Field::make('name')->activeUrl()

After (date)

The field value must be a value after a given date. See the Laravel documentation.

php
Field::make('startDate')->after('tomorrow')

Alternatively, you may pass the name of another field to compare against:

php
Field::make('startDate')
Field::make('endDate')->after('startDate')

After or equal to (date)

The field value must be a date after or equal to the given date. See the Laravel documentation.

php
Field::make('startDate')->afterOrEqual('tomorrow')

Alternatively, you may pass the name of another field to compare against:

php
Field::make('startDate')
Field::make('endDate')->afterOrEqual('startDate')

Alpha

The field must be entirely alphabetic characters. See the Laravel documentation.

php
Field::make('name')->alpha()

Alpha Dash

The field may have alpha-numeric characters, as well as dashes and underscores. See the Laravel documentation.

php
Field::make('name')->alphaDash()

Alpha Numeric

The field must be entirely alpha-numeric characters. See the Laravel documentation.

php
Field::make('name')->alphaNum()

ASCII

The field must be entirely 7-bit ASCII characters. See the Laravel documentation.

php
Field::make('name')->ascii()

Before (date)

The field value must be a date before a given date. See the Laravel documentation.

php
Field::make('startDate')->before('first day of next month')

Alternatively, you may pass the name of another field to compare against:

php
Field::make('startDate')->before('endDate')
Field::make('endDate')

Before or equal to (date)

The field value must be a date before or equal to the given date. See the Laravel documentation.

php
Field::make('startDate')->beforeOrEqual('end of this month')

Alternatively, you may pass the name of another field to compare against:

php
Field::make('startDate')->beforeOrEqual('endDate')
Field::make('endDate')

Confirmed

The field must have a matching field of {field}_confirmation. See the Laravel documentation.

php
Field::make('password')->confirmed()
Field::make('password_confirmation')

Different

The field value must be different to another. See the Laravel documentation.

php
Field::make('backupEmail')->different('email')

Doesnt Start With

The field must not start with one of the given values. See the Laravel documentation.

php
Field::make('name')->doesntStartWith(['admin'])

Doesnt End With

The field must not end with one of the given values. See the Laravel documentation.

php
Field::make('name')->doesntEndWith(['admin'])

Ends With

The field must end with one of the given values. See the Laravel documentation.

php
Field::make('name')->endsWith(['bot'])

Enum

The field must contain a valid enum value. See the Laravel documentation.

php
Field::make('status')->enum(MyStatus::class)

Exists

The field value must exist in the database. See the Laravel documentation..

php
Field::make('invitation')->exists()

By default, the form's model will be searched, if it is registered. You may specify a custom table name or model to search:

php
use App\Models\Invitation;

Field::make('invitation')->exists(table: Invitation::class)

By default, the field name will be used as the column to search. You may specify a custom column to search:

php
Field::make('invitation')->exists(column: 'id')

You can further customize the rule by passing a closure to the callback parameter:

php
use Illuminate\Validation\Rules\Exists;

Field::make('invitation')
    ->exists(callback: function (Exists $rule) {
        return $rule->where('is_active', 1);
    })

Filled

The field must not be empty when it is present. See the Laravel documentation.

php
Field::make('name')->filled()

Greater than

The field value must be greater than another. See the Laravel documentation.

php
Field::make('newNumber')->gt('oldNumber')

Greater than or equal to

The field value must be greater than or equal to another. See the Laravel documentation.

php
Field::make('newNumber')->gte('oldNumber')

In

The field must be included in the given list of values. See the Laravel documentation.

php
Field::make('status')->in(['pending', 'completed'])

Ip Address

The field must be an IP address. See the Laravel documentation.

php
Field::make('ip_address')->ip()
Field::make('ip_address')->ipv4()
Field::make('ip_address')->ipv6()

JSON

The field must be a valid JSON string. See the Laravel documentation.

php
Field::make('ip_address')->json()

Less than

The field value must be less than another. See the Laravel documentation.

php
Field::make('newNumber')->lt('oldNumber')

Less than or equal to

The field value must be less than or equal to another. See the Laravel documentation.

php
Field::make('newNumber')->lte('oldNumber')

Mac Address

The field must be a MAC address. See the Laravel documentation.

php
Field::make('mac_address')->macAddress()

Multiple Of

The field must be a multiple of value. See the Laravel documentation.

php
Field::make('number')->multipleOf(2)

Not In

The field must not be included in the given list of values. See the Laravel documentation.

php
Field::make('status')->notIn(['cancelled', 'rejected'])

Not Regex

The field must not match the given regular expression. See the Laravel documentation.

php
Field::make('email')->notRegex('/^.+$/i')

Nullable

The field value can be empty. This rule is applied by default if the required rule is not present. See the Laravel documentation.

php
Field::make('name')->nullable()

Prohibited

The field value must be empty. See the Laravel documentation.

php
Field::make('name')->prohibited()

Required

The field value must not be empty. See the Laravel documentation.

php
Field::make('name')->required()

Required If

The field value must not be empty only if the other specified field has any of the given values. See the Laravel documentation.

php
Field::make('name')->requiredIf('field', 'value')

Required Unless

The field value must not be empty unless the other specified field has any of the given values. See the Laravel documentation.

php
Field::make('name')->requiredUnless('field', 'value')

Required With

The field value must not be empty only if any of the other specified fields are not empty. See the Laravel documentation.

php
Field::make('name')->requiredWith('field,another_field')

Required With All

The field value must not be empty only if all of the other specified fields are not empty. See the Laravel documentation.

php
Field::make('name')->requiredWithAll('field,another_field')

Required Without

The field value must not be empty only when any of the other specified fields are empty. See the Laravel documentation.

php
Field::make('name')->requiredWithout('field,another_field')

Required Without All

The field value must not be empty only when all of the other specified fields are empty. See the Laravel documentation.

php
Field::make('name')->requiredWithoutAll('field,another_field')

Regex

The field must match the given regular expression. See the Laravel documentation.

php
Field::make('email')->regex('/^.+@.+$/i')

Same

The field value must be the same as another. See the Laravel documentation.

php
Field::make('password')->same('passwordConfirmation')

Starts With

The field must start with one of the given values. See the Laravel documentation.

php
Field::make('name')->startsWith(['a'])

String

The field must be a string. See the Laravel documentation.

php
Field::make('name')->string()

Unique

The field value must not exist in the database. See the Laravel documentation.

php
Field::make('email')->unique()

By default, the form's model will be searched, if it is registered. You may specify a custom table name or model to search:

php
use App\Models\User;

Field::make('email')->unique(table: User::class)

By default, the field name will be used as the column to search. You may specify a custom column to search:

php
Field::make('email')->unique(column: 'email_address')

Sometimes, you may wish to ignore a given model during unique validation. For example, consider an "update profile" form that includes the user's name, email address, and location. You will probably want to verify that the email address is unique. However, if the user only changes the name field and not the email field, you do not want a validation error to be thrown because the user is already the owner of the email address in question.

php
Field::make('email')->unique(ignorable: $ignoredUser)

If you're using the admin panel, you can easily ignore the current record by using ignoreRecord instead:

php
Field::make('email')->unique(ignoreRecord: true)

You can further customize the rule by passing a closure to the callback parameter:

php
use Illuminate\Validation\Rules\Unique;

Field::make('email')
    ->unique(callback: function (Unique $rule) {
        return $rule->where('is_active', 1);
    })

UUID

The field must be a valid RFC 4122 (version 1, 3, 4, or 5) universally unique identifier (UUID). See the Laravel documentation.

php
Field::make('identifer')->uuid()

Other rules

You may add other validation rules to any field using the rules() method:

php
TextInput::make('slug')->rules(['alpha_dash'])

A full list of validation rules may be found in the Laravel documentation.

Custom rules

You may use any custom validation rules as you would do in Laravel:

php
TextInput::make('slug')->rules([new Uppercase()])

You may also use closure rules:

php
TextInput::make('slug')->rules([
    function () {
        return function (string $attribute, $value, Closure $fail) {
            if ($value === 'foo') {
                $fail('The :attribute is invalid.');
            }
        };
    },
])

Validation attributes

When fields fail validation, their label is used in the error message. To customize the label used in field error messages, use the validationAttribute() method:

php
use Filament\Forms\Components\TextInput;

TextInput::make('name')->validationAttribute('full name')

Sending validation notifications

If you want to send a notification when validation error occurs, you may do so by using the onValidationError() method on your Livewire component:

php
use Filament\Notifications\Notification;
use Illuminate\Validation\ValidationException;

protected function onValidationError(ValidationException $exception): void
{
    Notification::make()
        ->title($exception->getMessage())
        ->danger()
        ->send();
}

Alternatively, if you are using admin panel and you want this behaviour on all the pages, add this inside the boot() method of your AppServiceProvider:

php
use Filament\Notifications\Notification;
use Filament\Pages\Page;
use Illuminate\Validation\ValidationException;

Page::$reportValidationErrorUsing = function (ValidationException $exception) {
    Notification::make()
        ->title($exception->getMessage())
        ->danger()
        ->send();
};

File: docs/06-advanced.md


title: Advanced

Using closure customization

All configuration methods for fields and layout components accept closures as parameters instead of hardcoded values:

php
use App\Models\User;
use Filament\Forms\Components\DatePicker;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;

DatePicker::make('date_of_birth')
    ->displayFormat(function () {
        if (auth()->user()->country_id === 'us') {
            return 'm/d/Y'
        } else {
            return 'd/m/Y'
        }
    })

Select::make('userId')
    ->options(function () {
        return User::all()->pluck('name', 'id');
    })

TextInput::make('middle_name')
    ->required(function () {
        return auth()->user()->hasMiddleName();
    })

This alone unlocks many customization possibilities.

The package is also able to inject many utilities to use inside these closures, as parameters.

If you wish to access the current state (value) of the component, define a $state parameter:

php
function ($state) {
    // ...
}

If you wish to access the current component instance, define a $component parameter:

php
use Filament\Forms\Components\Component;

function (Component $component) {
    // ...
}

If you wish to access the current Livewire component instance, define a $livewire parameter:

php
use Livewire\Component as Livewire;

function (Livewire $livewire) {
    // ...
}

If you have defined a form or component Eloquent model instance, define a $record parameter:

php
use Illuminate\Database\Eloquent\Model;

function (?Model $record) {
    // ...
}

You may also retrieve the value of another field from within a callback, using a closure $get parameter:

php
use Closure;

function (Closure $get) {
    $email = $get('email'); // Store the value of the `email` field in the `$email` variable.
    //...
}

In a similar way to $get, you may also set the value of another field from within a callback, using a closure $set parameter:

php
use Closure;

function (Closure $set) {
    $set('title', 'Blog Post'); // Set the `title` field to `Blog Post`.
    //...
}

If you're writing a form for an admin panel resource or relation manager, and you wish to check if a form is create, edit or view, use the $context parameter:

php
function (string $context) {
    // ...
}

Outside of the admin panel, you can set a form's context by defining a getFormContext() method on your Livewire component.

Callbacks are evaluated using Laravel's app()->call() under the hood, so you are able to combine multiple parameters in any order:

php
use Closure;
use Livewire\Component as Livewire;

function (Livewire $livewire, Closure $get, Closure $set) {
    // ...
}

Reloading the form when a field is updated

By default, forms are only reloaded when they are validated or submitted. You may allow a form to be reloaded when a field is changed, by using the reactive() method on that field:

php
use Filament\Forms\Components\TextInput;

TextInput::make('title')->reactive()

A great example to give a use case for this is when you wish to generate a slug from a title field automatically:

Dependant fields / components

You may use the techniques described in the closure customization section to build completely dependent fields and components, with full control over customization based on the values of other fields in your form.

For example, you can build dependant select inputs:

Sometimes, you may wish to conditionally hide any form component based on the value of a field. You may do this with a hidden() method:

php
use Closure;
use Filament\Forms\Components\TextInput;

TextInput::make('newPassword')
    ->password()
    ->reactive()

TextInput::make('newPasswordConfirmation')
    ->password()
    ->hidden(fn (Closure $get) => $get('newPassword') === null)

The field/s you're depending on should be reactive(), to ensure the Livewire component is reloaded when they are updated.

Field lifecycle

Hydration

Hydration is the process which fill fields with data. It runs when you call the form's fill() method. You may customize what happens after a field is hydrated using the afterStateHydrated().

In this example, the name field will always be hydrated with the correctly capitalized name:

php
use Closure;
use Filament\Forms\Components\TextInput;

TextInput::make('name')
    ->afterStateHydrated(function (TextInput $component, $state) {
        $component->state(ucwords($state));
    })

Updates

You may use the afterStateUpdated() method to customize what happens after a field is updated.

In this example, the slug field is updated with the slug version of the title field automatically:

php
use Closure;
use Filament\Forms\Components\TextInput;
use Illuminate\Support\Str;

TextInput::make('title')
    ->reactive()
    ->afterStateUpdated(function (Closure $set, $state) {
        $set('slug', Str::slug($state));
    })
TextInput::make('slug')

Dehydration

Dehydration is the process which gets data from fields, and transforms it. It runs when you call the form's getState() method. You may customize how the state is dehydrated from the form by returning the transformed state from the dehydrateStateUsing() callback.

In this example, the name field will always be dehydrated with the correctly capitalized name:

php
use Filament\Forms\Components\TextInput;

TextInput::make('name')->dehydrateStateUsing(fn ($state) => ucwords($state))

You may also prevent the field from being dehydrated altogether by passing false to dehydrated().

In this example, the passwordConfirmation field will not be present in the array returned from getData():

php
use Filament\Forms\Components\TextInput;

TextInput::make('passwordConfirmation')
    ->password()
    ->dehydrated(false)

Using form events

Forms can dispatch and listen to events, which allow the frontend and backend to communicate.

These events can be dispatched in a component view, and then listened to by a component's class.

Dispatching events

To dispatch a form event, call the dispatchFormEvent() Livewire method with the event name:

blade
Save

Usually, you will want to dispatch events for a specific component class. In this case, you should prefix the event name with the component name, and pass the component's state path as a second parameter:

blade
Add item

You may also pass other parameters to the event:

blade
Delete item

Listening to events

You may register listeners for form events by calling the registerListeners() method when your component is setUp():

php
use Filament\Forms\Components\Component;

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

    $this->registerListeners([
        'save' => [
            function (Component $component): void {
                // ...
            },
        ],
    ]);
}

If your event is component-specific, you'll want to ensure that the component is not disabled, and that the component's state path matches the event's second parameter:

php
use Filament\Forms\Components\Component;

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

    $this->registerListeners([
        'repeater::createItem' => [
            function (Component $component, string $statePath): void {
                if ($component->isDisabled()) {
                    return;
                }

                if ($statePath !== $component->getStatePath()) {
                    return;
                }

                // ...
            },
        ],
    ]);
}

Additionally, you may receive other parameters from the event:

php
use Filament\Forms\Components\Component;

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

    $this->registerListeners([
        'repeater::deleteItem' => [
            function (Component $component, string $statePath, string $uuidToDelete): void {
                if ($component->isDisabled()) {
                    return;
                }

                if ($statePath !== $component->getStatePath()) {
                    return;
                }

                // Delete item with UUID `$uuidToDelete`
            },
        ],
    ]);
}

File: docs/07-testing.md


title: Testing

All examples in this guide will be written using Pest. However, you can easily adapt this to PHPUnit.

Since the form builder works on Livewire components, you can use the Livewire testing helpers. However, we have custom testing helpers that you can use with forms:

Filling a form

To fill a form with data, pass the data to fillform():

php
use function Pest\Livewire\livewire;

livewire(CreatePost::class)
    ->fillForm([
        'title' => fake()->sentence(),
        // ...
    ]);

Note that if you have multiple forms on a Livewire component, you can specify which form you want to fill using fillForm([...], 'createPostForm').

To check that a form has data, use assertFormSet():

php
use Illuminate\Support\Str;
use function Pest\Livewire\livewire;

it('can automatically generate a slug from the title', function () {
    $title = fake()->sentence();

    livewire(CreatePost::class)
        ->fillForm([
            'title' => $title,
        ])
        ->assertFormSet([
            'slug' => Str::slug($title),
        ]);
});

Note that if you have multiple forms on a Livewire component, you can specify which form you want to check using assertFormSet([...], 'createPostForm').

Validation

Use assertHasFormErrors() to ensure that data is properly validated in a form:

php
use function Pest\Livewire\livewire;

it('can validate input', function () {
    livewire(CreatePost::class)
        ->fillForm([
            'title' => null,
        ])
        ->assertHasFormErrors(['title' => 'required']);
});

And assertHasNoFormErrors() to ensure there are no validation errors:

php
use function Pest\Livewire\livewire;

livewire(CreatePost::class)
    ->fillForm([
        'title' => fake()->sentence(),
        // ...
    ])
    ->call('save')
    ->assertHasNoFormErrors();

Note that if you have multiple forms on a Livewire component, you can pass the name of a specific form as the second parameter like assertHasFormErrors(['title' => 'required'], 'createPostForm') or assertHasNoFormErrors([], 'createPostForm').

Form existence

To check that a Livewire component has a form, use assertFormExists():

php
use function Pest\Livewire\livewire;

it('has a form', function () {
    livewire(CreatePost::class)
        ->assertFormExists();
});

Note that if you have multiple forms on a Livewire component, you can pass the name of a specific form like assertFormExists('createPostForm').

Fields

To ensure that a form has a given field pass the field name to assertFormFieldExists():

php
use function Pest\Livewire\livewire;

it('has a title field', function () {
    livewire(CreatePost::class)
        ->assertFormFieldExists('title');
});

You may pass a function as an additional argument in order to assert that a field passes a given "truth test". This is useful for asserting that a field has a specific configuration:

php
use function Pest\Livewire\livewire;

it('has a title field', function () {
    livewire(CreatePost::class)
        ->assertFormFieldExists('title', function (TextInput $field): bool {
            return $input->isDisabled();
        });
});

Note that if you have multiple forms on a Livewire component, you can specify which form you want to check for the existence of the field like assertFormFieldExists('title', 'createPostForm').

Hidden fields

To ensure that a field is visible pass the name to assertFormFieldIsVisible():

php
use function Pest\Livewire\livewire;

test('title is visible', function () {
    livewire(CreatePost::class)
        ->assertFormFieldIsVisible('title');
});

Or to ensure that a field is hidden you can pass the name to assertFormFieldIsHidden():

php
use function Pest\Livewire\livewire;

test('title is hidden', function () {
    livewire(CreatePost::class)
        ->assertFormFieldIsHidden('title');
});

Note that for both assertFormFieldIsHidden() and assertFormFieldIsVisible() you can pass the name of a specific form the field belongs to as the second argument like assertFormFieldIsHidden('title', 'createPostForm').

Disabled fields

To ensure that a field is enabled pass the name to assertFormFieldIsEnabled():

php
use function Pest\Livewire\livewire;

test('title is enabled', function () {
    livewire(CreatePost::class)
        ->assertFormFieldIsEnabled('title');
});

Or to ensure that a field is disabled you can pass the name to assertFormFieldIsDisabled():

php
use function Pest\Livewire\livewire;

test('title is disabled', function () {
    livewire(CreatePost::class)
        ->assertFormFieldIsDisabled('title');
});

Note that for both assertFormFieldIsEnabled() and assertFormFieldIsDisabled() you can pass the name of a specific form the field belongs to as the second argument like assertFormFieldIsEnabled('title', 'createPostForm').