File: README.md
Build apps & admin panels fast, for your bright ideas.
With a solid Laravel foundation and a polished UI, you can focus on what makes your product unique. Filament gives you UI components that you won't outgrow — a cohesive set of well-considered building blocks that adapt as your application grows in complexity.
- Tables — Browse and filter large datasets with powerful columns, actions, and bulk operations.
- Forms — Build complex, reactive forms using a set of reusable, state-aware components.
- Infolists — Render read-only record views with structured layouts and custom formatting.
- Notifications — Trigger in-app feedback for actions, errors, and system events with minimal setup.
- Dashboard widgets — Surface key metrics and trends using live, data-driven widgets tailored to each user.
- Action modals — Handle confirmations and data entry with focused modal workflows tied to actions.
Get Started • Launch Demo • Join Discord
Contributing
Please see our contributing guide.
Need Help?
🐞 If you spot a bug, please submit a detailed issue, and wait for assistance.
🤔 If you have a question or feature request, please start a new discussion. We also have a Discord community. For quick help, ask questions in the appropriate channel.
🔐 If you discover a vulnerability, please review our security policy.
File: docs/01-getting-started.md
title: Getting Started
Filament is a content management framework for rapidly building a beautiful administration interface designed for humans.
Filament requires Laravel 8.x or higher, and PHP 7.4 or higher.
Installation:
composer require filament/filament
php artisan migrateCreate an administrator account for your admin panel by running
php artisan make:filament-userand answering the input prompts. Administrators have access to all areas of Filament, and are able to manage other users.
Once you have a user account, you can sign in to the admin panel by visiting /admin in your browser.
To start building your admin panel, create a resource.
Configuration
If you'd like to expose advanced configuration options for Filament, you may publish its configuration file:
php artisan vendor:publish --tag=filament-configIf you have published the configuration file for Filament, please ensure that you republish it when you upgrade.
Users
By default, Filament includes its own authentication guard and users table that is completely separate from your app's users table. This enables you to get up and running with Filament at record speed.
Some projects may choose to allow their app users access to Filament. In this case, they may customize the auth guard that Filament uses by configuring auth.guard to the name of your default guard, typically web.
The next step is to prepare your User model for use with Filament. Implement the Filament\Models\Contracts\FilamentUser interface, and apply the Filament\Models\Concerns\IsFilamentUser trait. These provide Filament with an API that it can use to interact with your existing user data:
<?php
namespace App\Models;
use Filament\Models\Concerns\IsFilamentUser;
use Filament\Models\Contracts\FilamentUser;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable implements FilamentUser
{
use IsFilamentUser;
// ...
}By default, all users will be able to sign in to Filament. To change this, you may set the static $filamentUserColumn property on your custom user class to the name of a boolean column in your database:
public static $filamentUserColumn = 'is_filament_user'; // The name of a boolean column in your database.Alternatively, you may override the canAccessFilament() on your custom user class, returning a boolean:
public function canAccessFilament()
{
return $this->group === 'Filament Users';
}Filament implements authorization features in its default users table. Admin users are able to access all areas of Filament, and manage other users. Users may have roles, which are associated with certain permissions in your admin panel.
To configure columns to granting users admin permissions and roles, you may set the static $filamentAdminColumn and $filamentRolesColumn properties on your class:
public static $filamentAdminColumn = 'is_filament_admin'; // The name of a boolean column in your database.
public static $filamentRolesColumn = 'filament_roles'; // The name of a JSON column in your database.To disable roles and admin features, just omit these properties from your class.
Alternatively, you may specify custom logic for calculating if a user has admin permissions by overriding the isFilamentAdmin() method:
public function isFilamentAdmin()
{
return $this->email === '[email protected]';
}Finally, for the correct reset password URL to be sent, you should implement a sendPasswordResetNotification($token) function. You can use the Filament\Models\Concerns\SendsFilamentPasswordResetNotification trait to add this functionality to your User model, fully integrated with Filament:
<?php
namespace App\Models;
use Filament\Models\Concerns\IsFilamentUser;
use Filament\Models\Concerns\SendsFilamentPasswordResetNotification;
use Filament\Models\Contracts\FilamentUser;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable implements FilamentUser
{
use IsFilamentUser;
use SendsFilamentPasswordResetNotification;
// ...
}Disabling the Default Migrations
You may wish to prevent the migration for the default users table from being registered. You may do this by calling:
use Filament\Filament;
Filament::ignoreMigrations();from the register() method of your AppServiceProvider.
Stubs
Filament commands use stubs as templates when creating new files in your project. You may customize these stubs by publishing them to your app:
php artisan vendor:publish --tag=filament-stubsIf you have published the stubs for Filament, please ensure that you republish them when you upgrade.
Upgrade Guide
To upgrade Filament to the latest version, you may run:
php artisan filament:upgradeor the following commands manually:
composer update
php artisan migrate
php artisan livewire:discover
php artisan route:clear
php artisan view:clearFile: docs/02-resources.md
title: Resources
Resources are static classes that describe how administrators should be able to interact with data from your app. They are associated with Eloquent models from your app.
To create a resource for the App\Models\Customer model:
php artisan make:filament-resource CustomerThis will create several files in the app/Filament/Resources directory:
.
+-- CustomerResource.php
+-- CustomerResource
| +-- Pages
| | +-- CreateCustomer.php
| | +-- EditCustomer.php
| | +-- ListCustomers.phpYour new resource class lives in CustomerResource.php. Resource classes register forms, tables, authorization settings, and pages associated with that model.
The classes in the Pages directory are used to customize the pages in the admin panel that interact with your resource.
By default, the model associated with your resource is guessed based on the class name of the resource. You may set the static $model property to disable this behaviour:
public static $model = Customer::class;A label for this resource is generated based on the name of the resource's model. It's used the navigation menu and to display breadcrumbs. You may customize it using the static $label property:
public static $label = 'customer';Forms
Resource classes contain a static form() method that is used to customize the forms to create and update resource records.
use Filament\Resources\Forms\Form;
public static function form(Form $form)
{
return $form
->schema([
// ...
]);
}The schema() method is used to define the structure of your form. It is an array of components, in the order they should appear in your form.
For more information, please see the page on Building Forms.
Relations
Managing Single Related Records
The Filament\Resources\Forms\Components\BelongsToSelect field can be used in resource form schemas to create a select element with options to search and select a related record. It has the same methods available as Filament\Resources\Forms\Components\Select, and others to define the relationship and column name that should be used:
Components\BelongsToSelect::make('category_id')
->relationship('category', 'name');This example assumes the following:
- A
category_idforeign key column exists on your parent model. - A
belongsTo()categoryrelationship on your parent model. - A
namecolumn on your category model that can be used to render the list of categories to select from.
Sometimes, having a lot of related records as options can cause strain on your web browser. By default, options will only load when you start typing a search. To change this, you may preload() a select field with options. Please only do this if you are certain that there are only a few related records to choose from:
Components\BelongsToSelect::make('category_id')
->relationship('category', 'name')
->preload();You may also customize the Query Builder used to get search results by specifying a callback in the third parameter of the relationship() method.
Components\BelongsToSelect::make('category_id')
->relationship('category', 'name', function ($query) {
return $query->where('is_featured', true);
});This example will only include featured categories in search results.
Managing Multiple Related Records
Relation managers are components that allow administrators to list, create, attach, edit, detach and delete related records without leaving the parent record's edit page. Resource classes contain a static relations() method that is used to register relation managers for your resource.
To create a relation manager, you can use:
php artisan make:filament-relation-manager CustomerResource ordersThis will create a CustomerResource/RelationManagers/OrdersRelationManager.php file. This contains a class where you are able to define a form and table for your relation manager. The relation manager will interact with the orders relationship on your parent model.
You must set the primary column of related records using the static $primaryColumn property on your new relation manager class. The primary column is used to identify related records quickly. This could be a user's name, or a blog post's title.
<?php
namespace App\Filament\Resources\CustomerResource\RelationManagers;
use Filament\Resources\RelationManager;
class OrdersRelationManager extends RelationManager
{
public static $primaryColumn = 'name';
public static $relationship = 'orders';
}You must register the new relation manager in your resource's relations() method:
public static function relations()
{
return [
RelationManagers\OrdersRelationManager::class,
];
}Once a table and form have been defined for the relation manager, visit the edit page of your resource to see it in action.
HasMany, BelongsToMany and MorphMany relationships are currently fully supported by relation managers.
BelongsToMany relation managers require an extra static $inverseRelationship property set on the class if the existing relationships deviate from traditional naming conventions:
public static $inverseRelationship = 'products';Tables
Resource classes contain a static table() method that is used to customize the table to list resource records.
use Filament\Resources\Tables\Table;
public static function table(Table $table)
{
return $table
->columns([
// ...
])
->filters([
// ...
]);
}The columns() method is used to define the columns in your table. It is an array of column objects, in the order they should appear in your table.
Filters are predefined scopes that administrators can use to filter records in your table. The filters() method is used to register these.
For more information, please see the page on Building Tables.
Pages
Pages are classes that are associated with a resource. They are essentially Laravel Livewire components with custom integration utilities for use with Filament.
Page class files are in the /Pages directory of your resource directory.
By default, resources are generated with three pages:
- List has a table for displaying, searching and deleting resource records. From here, you are able to access the create and edit pages. It is routed to
/. - Create has a form that is able to create a resource record. It is routed to
/create. - Edit has a form that is able to update a resource record, along with the relation managers registered to your resource. It is routed to
/{record}/edit.
Customizing Default Pages
You are able to customize text used in the default pages by overriding properties on the page class. To see the options available, check the static properties defined in the parent class of each default page.
For further customization opportunities, you can override the static $view property on your page to a custom view in your app:
public static $view = 'customers.list-records';Customizing Form Redirects
You may specify a custom redirect URL for the Create and Edit pages by overriding the getRedirectUrl() method.
For example, the Create form can redirect back to the List page when it is submitted:
protected function getRedirectUrl(Model $record): string
{
return $this->getResource()::generateUrl('index');
}Hooks
Hooks may be used to customize the behaviour of a default page. To set up a hook, create a protected method on the page class with the name of the hook:
protected function beforeSave()
{
// ...
}In this example, the code in the beforeSave() method will be called before the data in the form is saved to the database.
There are several available hooks for the create and edit pages:
use Filament\Resources\Pages\CreateRecord;
class CreateCustomer extends CreateRecord
{
// ...
protected function beforeFill()
{
// Runs before the form fields are populated with their default values.
}
protected function afterFill()
{
// Runs after the form fields are populated with their default values.
}
protected function beforeValidate()
{
// Runs before the form fields are validated when the form is submitted.
}
protected function afterValidate()
{
// Runs after the form fields are validated when the form is submitted.
}
protected function beforeCreate()
{
// Runs before the form fields are saved to the database.
}
protected function afterCreate()
{
// Runs after the form fields are saved to the database.
}
}use Filament\Resources\Pages\EditRecord;
class EditCustomer extends EditRecord
{
// ...
protected function beforeFill()
{
// Runs before the form fields are populated from the database.
}
protected function afterFill()
{
// Runs after the form fields are populated from the database.
}
protected function beforeValidate()
{
// Runs before the form fields are validated when the form is saved.
}
protected function afterValidate()
{
// Runs after the form fields are validated when the form is saved.
}
protected function beforeSave()
{
// Runs before the form fields are saved to the database.
}
protected function afterSave()
{
// Runs after the form fields are saved to the database.
}
protected function beforeDelete()
{
// Runs before the record is deleted.
}
protected function afterDelete()
{
// Runs after the record is deleted.
}
}Custom Pages
Filament allows you to create completely custom pages for resources. To create a new page, you can use:
php artisan make:filament-page SortCustomers --resource=CustomerResourceThis command will create two files - a page class in the /Pages directory of your resource directory, and a view in the /pages directory of the resource views directory.
You must register custom pages to a route in the static routes() method of your resource:
public static function routes()
{
return [
// ...
Pages\SortCustomers::routeTo('/sort', 'sort'),
];
}The first parameter of the routeTo() method is the path of the route, and the second is its name. Any parameters defined in the route's path will be available to the page class, in an identical way to Livewire.
To generate a URL for a resource route, you may call the static generateUrl() method on the page class:
SortCustomers::generateUrl($parameters = [], $absolute = true);To access a custom page, such as a "show" route, from the table, you can define a new action:
use Filament\Resources\Tables\Table;
use Filament\Tables\RecordActions\Link;
public static function table(Table $table)
{
return $table
// ...
->prependRecordActions([
Link::make('view')->url(fn ($record) => static::generateUrl('show', ['record' => $record])),
]);
}By passing a callback to the url() method, you can get access to each row's record.
Authorization
For authorization, Filament will observe any model policies that are registered in your app. The viewAny action may be used to completely disable resources and remove them from the navigation menu.
Filament also includes a powerful role-based authorization system, which is set up out of the box with the default users table. You may also implement roles functionality in a custom users table.
You may create roles, such as Manager, using:
php artisan make:filament-role ManagerAdministrators will now be able to assign this role to any Filament user through the admin panel.
To only allow users with the Manager role to access a resource, declare so in the static authorization() method:
use App\Filament\Roles;
public static function authorization()
{
return [
Roles\Manager::allow(),
];
}You may authorize as many roles as you wish.
Please note: administrators will always have full access to every resource in your admin panel.
You may want to only deny users with the Manager role from accessing this resource. To do this, you may use the static deny() method instead:
use App\Filament\Roles;
public static function authorization()
{
return [
Roles\Manager::deny(),
];
}You may specify only() certain actions that roles have access to. These follow the same naming conventions as methods in a policy.
use App\Filament\Roles;
public static function authorization()
{
return [
Roles\Manager::allow()->only(['viewAny', 'create']),
];
}There is also the possibility to allow access to all actions except() those specified:
use App\Filament\Roles;
public static function authorization()
{
return [
Roles\Manager::allow()->except(['delete']),
];
}File: docs/03-forms.md
title: Building Forms
Filament comes with a powerful form builder which can be used to create intuitive, dynamic, and contextual forms in the admin panel.
Forms have a schema, which is an array that contains many form components. The schema defines the form's fields, their validation rules, and their layout in the form.
Here is an example form configuration for a CustomerResource:
use Filament\Resources\Forms\Components;
use Filament\Resources\Forms\Form;
public static function form(Form $form)
{
return $form
->schema([
Components\TextInput::make('name')->autofocus()->required(),
Components\TextInput::make('email')->email()->required(),
Components\Select::make('type')
->placeholder('Select a type')
->options([
'individual' => 'Individual',
'organization' => 'Organization',
]),
Components\DatePicker::make('birthday'),
])
->columns(2);
}Please note: when building forms for resources, please ensure that you are using components within the
Filament\Resources\Forms\Componentsnamespace and notFilament\Forms\Components.
Fields
Resource field classes are located in the Filament\Resources\Forms\Components namespace.
All field components have access to the following customization methods:
Field::make($name)
->columnSpan($span = 1) // On large devices, this sets the number of columns that the field should span in the form.
->default($default) // Sets the default value for this field.
->dependable() // Reloads the form when this field is changed.
->disabled($disabled = false) // Make the field read-only.
->extraAttributes($attributes = []) // A key-value array of extra HTML attributes to pass to the field.
->helpMessage($message) // Sets an optional message below the field. It supports Markdown.
->hint($hint) // Sets an optional short message adjacent to the label. It supports Markdown.
->id($id) // Set the HTML ID of the field, which is otherwise automatically generated based on its name.
->label($label); // Set custom label text for with the field, which is otherwise automatically generated based on its name. It supports localization strings.Checkbox
Checkbox::make($name)
->autofocus() // Autofocus the field.
->inline() // Render the checkbox inline with its label.
->stacked(); // Render the checkbox under its label.Date Picker
DatePicker::make($name)
->autofocus() // Autofocus the field.
->displayFormat($format = 'F j, Y') // Set the display format of the field, using PHP date formatting tokens.
->firstDayOfWeek($day = 1) // Set the first day of the week in the calendar view, with 1 being Monday, and 0 or 7 being Sunday.
->format($format = 'Y-m-d') // Set the storage format of the field, using PHP date formatting tokens.
->maxDate($date) // Set the maximum date that can be selected.
->minDate($date) // Set the minimum date that can be selected.
->placeholder($placeholder) // Set the placeholder for when the field is empty. It supports localization strings.
->weekStartsOnMonday() // Set the first day of the week to Monday in the calendar view.
->weekStartsOnSunday(); // Set the first day of the week to Sunday in the calendar view.Date-time Picker
DateTimePicker::make($name)
->autofocus() // Autofocus the field.
->displayFormat($format = 'F j, Y H:i:s') // Set the display format of the field, using PHP date formatting tokens.
->firstDayOfWeek($day = 1) // Set the first day of the week in the calendar view, with 1 being Monday, and 0 or 7 being Sunday.
->format($format = 'Y-m-d H:i:s') // Set the storage format of the field, using PHP date formatting tokens.
->maxDate($date) // Set the maximum date that can be selected.
->minDate($date) // Set the minimum date that can be selected.
->placeholder($placeholder) // Set the placeholder for when the field is empty. It supports localization strings.
->weekStartsOnMonday() // Set the first day of the week to Monday in the calendar view.
->weekStartsOnSunday() // Set the first day of the week to Sunday in the calendar view.
->withoutSeconds(); // Hide the seconds input.File Upload
/* Detailed source-code truncated for AI context efficiency. */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.
To customize Livewire's default file upload validation rules, please refer to its documentation.
Available values for the position methods can be found on Filepond's website.
Support for multiple file uploads is coming soon.
Key-value
KeyValue::make($name)
->addButtonLabel($label) // Set the add button label. It supports localization strings.
->deleteButtonLabel($label) // Set the delete button label. It supports localization strings.
->disableAddingRows($state = false) // Disable the addition of rows.
->disableDeletingRows($state = false) // Disable the deletion of rows.
->disableEditingKeys($state = false) // Disable the editing of keys.
->keyLabel($label) // Set the key field label label. It supports localization strings.
->keyPlaceholder($placeholder) // Set the key field placeholder. It supports localization strings.
->sortable($sortable = true) // Allow the keys to be sorted using drag and drop.
->sortButtonLabel($label) // Set the sort button label. It supports localization strings.
->valueLabel($label) // Set the value field label label. It supports localization strings.
->valuePlaceholder($placeholder); // Set the value field placeholder. It supports localization strings.Markdown Editor
MarkdownEditor::make($name)
->attachmentDisk($disk) // Set a custom disk that uploaded attachments should be read from and written to.
->attachmentDirectory($directory) // Set a custom directory that uploaded attachments should be written to.
->autofocus() // Autofocus the field.
->disableAllToolbarButtons() // Disable all toolbar buttons.
->disableToolbarButtons($buttons = []) // Disable toolbar buttons. See below for options.
->enableToolbarButtons($buttons = []) // Enable toolbar buttons. See below for options.
->placeholder($placeholder); // Set the placeholder for when the field is empty. It supports localization strings.Toolbar Buttons
attachFiles
bold
bullet
code
italic
link
number
preview
strike
writeRich Editor
RichEditor::make($name)
->attachmentDisk($disk) // Set a custom disk that uploaded attachments should be read from and written to.
->attachmentDirectory($directory) // Set a custom directory that uploaded attachments should be written to.
->autofocus() // Autofocus the field.
->disableAllToolbarButtons() // Disable all toolbar buttons.
->disableToolbarButtons($buttons = []) // Disable toolbar buttons. See below for options.
->enableToolbarButtons($buttons = []) // Enable toolbar buttons. See below for options.
->placeholder($placeholder); // Set the placeholder for when the field is empty. It supports localization strings.Toolbar Buttons
attachFiles
bold
bullet
code
heading
italic
link
number
quote
redo
strike
subheading
title
undoSelect
Select::make($name)
->autofocus() // Autofocus the field.
->emptyOptionsMessage($message) // Set the message for when there are no options available to pick from. It supports localization strings.
->noSearchResultsMessage($message) // Set the message for when there are no option search results. It supports localization strings.
->options($options = []) // Set the key-value array of available options to pick from.
->placeholder($placeholder); // Set the placeholder for when the field is empty. It supports localization strings.If you're looking to use a select for a
belongsTo()relationship, please check out theBelongsToSelectresource field.
Tags Input
TagsInput::make($name)
->autofocus() // Autofocus the field.
->placeholder($placeholder) // Set the placeholder for when the new tag field is empty. It supports localization strings.
->separator($separator = ','); // Set the separator that should be used between tags.Textarea
Textarea::make($name)
->autocomplete($autocomplete = 'on') // Set up autocomplete for the field.
->autofocus() // Autofocus the field.
->cols($cols) // The number of columns wide the textarea is.
->disableAutocomplete() // Disable autocomplete for the field.
->placeholder($placeholder); // Set the placeholder for when the field is empty. It supports localization strings.
->rows($rows) // The number of rows tall the textarea is.Text Input
TextInput::make($name)
->autocomplete($autocomplete = 'on') // Set up autocomplete for the field.
->autofocus() // Autofocus the field.
->disableAutocomplete() // Disable autocomplete for the field.
->email() // Require a valid email address to be provided.
->max($max) // Set a maximum numeric value to be provided.
->min($min) // Set a minimum numeric value to be provided.
->numeric() // Require a numeric value to be provided.
->password() // Obfuscate the field's value.
->placeholder($placeholder) // Set the placeholder for when the field is empty. It supports localization strings.
->postfix($postfix) // Set a postfix label to be displayed after the input.
->prefix($prefix) // Set a prefix label to be displayed before the input.
->tel() // Require a valid telephone number to be provided.
->type($type = 'text') // Set the input's HTML type.
->url(); // Require a valid URL to be provided.Toggle
The onIcon() and offIcon() methods support the name of any Blade icon component, and passes a set of formatting classes to it. By default, the Blade Heroicons package is installed, so you may use the name of any Heroicon out of the box. However, you may create your own custom icon components or install an alternative library if you wish.
Toggle::make($name)
->autofocus() // Autofocus the field.
->inline() // Render the toggle inline with its label.
->offIcon($icon) // Set the icon that should be displayed when the toggle is off.
->onIcon($icon) // Set the icon that should be displayed when the toggle is on.
->stacked(); // Render the toggle under its label.Validation
Filament provides a number of validation methods that can be applied to fields. Please refer to the Laravel Validation docs if you are unsure about any of these.
->acceptedFileTypes($types = []) // Accepts an array of mime types, file upload field only.
->confirmed($field = '{field name}Confirmation') // Text-based fields only.
->email() // Text input field only.
->image() // File upload field only.
->max($value) // Text input field only.
->maxDate($date) // Date-based fields only.
->maxLength($length) // Text-based fields only.
->maxSize($size) // In kilobytes, file upload field only.
->min($value) // Text input field only.
->minDate($date) // Date-based fields only.
->minLength($length) // Text-based fields only.
->minSize($size) // In kilobytes, file upload field only.
->nullable() // Applied to all fields by default.
->numeric() // Text input field only.
->required()
->requiredWith()
->same($field) // Text-based fields only.
->tel() // Text input field only.
->unique($table, $column = '{field name}', $exceptCurrentRecord = false)
->url() // Text input field only.You may apply additional custom validation rules to any field using the rules() method:
Field($name)
->rules(['alpha', 'ends_with:a']);Please note: when specifying resource field names in custom validation rules, you must prefix them with
record..
Layout
Grid
By default, form fields are stacked on top of each other in one column. To change this across the entire form, you may chain the columns() method onto the form object:
use Filament\Resources\Forms\Form;
public static function form(Form $form)
{
return $form
->schema([
// ...
])
->columns(2);
}Alternatively, you may customize the number of columns for a small part of the form using a Grid component:
use Filament\Resources\Forms\Components;
use Filament\Resources\Forms\Form;
public static function form(Form $form)
{
return $form
->schema([
// ...
Components\Grid::make([
// ...
])->columns(2),
]);
}Section
You may want to separate your fields into sections, each with a heading and subheading. To do this, you can use a Section component:
use Filament\Resources\Forms\Components;
use Filament\Resources\Forms\Form;
public static function form(Form $form)
{
return $form
->schema([
// ...
Components\Section::make(
'Heading',
'Subheading',
[
// ...
],
),
]);
}If you don't require a subheading, you may use the schema() method to declare the section schema late:
use Filament\Resources\Forms\Components;
use Filament\Resources\Forms\Form;
public static function form(Form $form)
{
return $form
->schema([
// ...
Components\Section::make('Heading')
->schema([
// ...
]),
]);
}You may use the columns() method to easily create a grid within the section:
use Filament\Resources\Forms\Components;
use Filament\Resources\Forms\Form;
public static function form(Form $form)
{
return $form
->schema([
// ...
Components\Section::make(
'Heading',
'Subheading',
[
// ...
],
)->columns(2),
]);
}Sections may be collapsible() to optionally hide content in long forms:
use Filament\Resources\Forms\Components;
use Filament\Resources\Forms\Form;
public static function form(Form $form)
{
return $form
->schema([
// ...
Components\Section::make(
'Heading',
'Subheading',
[
// ...
],
)->collapsible(),
]);
}You may collapse() sections by default:
use Filament\Resources\Forms\Components;
use Filament\Resources\Forms\Form;
public static function form(Form $form)
{
return $form
->schema([
// ...
Components\Section::make(
'Heading',
'Subheading',
[
// ...
],
)->collapsed(),
]);
}Fieldset
You may want to group fields into a Fieldset. Each fieldset has a label, a border, and a two-column grid:
use Filament\Resources\Forms\Components;
use Filament\Resources\Forms\Form;
public static function form(Form $form)
{
return $form
->schema([
// ...
Components\Fieldset::make(
'Label',
[
// ...
],
),
]);
}You may use the columns() method to customize the number of columns in the fieldset:
use Filament\Resources\Forms\Components;
use Filament\Resources\Forms\Form;
public static function form(Form $form)
{
return $form
->schema([
// ...
Components\Fieldset::make(
'Label',
[
// ...
],
)->columns(3),
]);
}Tabs
Some forms can be long and complex. You may want to use tabs to reduce the number that are available at once:
use Filament\Resources\Forms\Components;
use Filament\Resources\Forms\Form;
public static function form(Form $form)
{
return $form
->schema([
// ...
Components\Tabs::make('Label')
->tabs([
Components\Tab::make(
'First Tab',
[
// ...
],
),
Components\Tab::make(
'Second Tab',
[
// ...
],
),
]),
]);
}You may use the columns() method to easily create a grid within the tab:
use Filament\Resources\Forms\Components;
use Filament\Resources\Forms\Form;
public static function form(Form $form)
{
return $form
->schema([
// ...
Components\Tabs::make('Label')
->tabs([
Components\Tab::make(
'Tab',
[
// ...
],
)->columns(2),
]),
]);
}Group
Groups are used to wrap multiple associated form components. They have no effect on the form visually, but are useful for applying modifications to many fields at once:
use Filament\Resources\Forms\Components;
use Filament\Resources\Forms\Form;
public static function form(Form $form)
{
return $form
->schema([
// ...
Components\Group::make([
// ...
]),
]);
}Placeholder
Placeholders can be used to render text-only "fields" within your forms. Each placeholder has a value, which is cannot be changed by the user.
use Filament\Resources\Forms\Components;
use Filament\Resources\Forms\Form;
public static function form(Form $form)
{
return $form
->schema([
// ...
Components\Placeholder::make('website', 'filamentadmin.com'),
]);
}Dependent Fields
Dependent fields are fields that are modified based on the value of another. For example, you could show a group of fields based on the value of a Select.
The first step to setting up dependent fields is to apply the dependable() method to the field that should be watched for changes. When the value of this field is changed, the whole form will reload:
Components\Select::make('type')
->placeholder('Select a type')
->options([
'individual' => 'Individual',
'organization' => 'Organization',
])
->dependable();To modify fields based on the value of another, you may use the when() method. The first argument to this method is a callback that evaluates the $record object, and returns true or false depending on if the modifications should be applied. The second argument makes modifications to the current field. If no second argument is supplied, the field will only be shown when the callback in the first argument is true:
In this example, the fields in the group will only be shown when the type field is set to individual:
Components\Group::make([
// ...
])->when(fn ($record) => $record->type === 'individual');Here, the company_number field will only be required when the type field is set to organization:
Components\TextInput::make('company_number')
->when(
fn ($record) => $record->type === 'organization',
fn ($field) => $field->required(),
);Context Customization
You may customize forms based on the page they are used. To do this, you can chain the only() or except() methods onto any form component.
use App\Filament\Resources\CustomerResource\Pages;
use Filament\Resources\Forms\Components;
use Filament\Resources\Forms\Form;
public static function form(Form $form)
{
return $form
->schema([
Components\TextInput::make('name')
->required()
->only(Pages\CreateCustomer::class),
]);
}In this example, the name field will only() be displayed on the CreateCustomer page.
use App\Filament\Resources\CustomerResource\Pages;
use Filament\Resources\Forms\Components;
use Filament\Resources\Forms\Form;
public static function form(Form $form)
{
return $form
->schema([
Components\TextInput::make('name')
->except(Pages\EditCustomer::class, fn ($field) => $field->required()),
]);
}In this example, the name field will be required, except() on the EditCustomer page.
This is an incredibly powerful pattern, and allows you to completely customize a form contextually by chaining as many methods as you wish to the callback.
Developing Custom Components
To create a custom field, you may use:
php artisan make:filament-field CountrySelect --resourceThis will create a new custom class and view for your field, which you may use in a form in the same way as any other field.
To create a generic form component, which may be commonly used for custom layouts, you may generate a class and view using:
php artisan make:filament-form-component SidebarLayout --resourceAlternatively, simple custom layouts may be created using a View component, and passing the name of a $view in your app:
Components\View::make($view);File: docs/04-tables.md
title: Building Tables
Filament includes a table builder which can be used to create interactive tables in the admin panel.
Tables have columns and filters, which are defined in two methods on the table object.
Here is an example table configuration for a CustomerResource:
use Filament\Resources\Tables\Columns;
use Filament\Resources\Tables\Filter;
use Filament\Resources\Tables\Table;
public static function table(Table $table)
{
return $table
->columns([
Columns\Text::make('name')->primary(),
Columns\Text::make('email')->url(fn ($customer) => "mailto:{$customer->email}"),
Columns\Text::make('type')
->options([
'individual' => 'Individual',
'organization' => 'Organization',
]),
Columns\Text::make('birthday')->date(),
Columns\Boolean::make('is_active')->label('Active?'),
])
->filters([
Filter::make('individuals', fn ($query) => $query->where('type', 'individual')),
Filter::make('organizations', fn ($query) => $query->where('type', 'organization')),
Filter::make('active', fn ($query) => $query->where('is_active', true)),
]);
}Columns
Resource column classes are located in the Filament\Resources\Tables\Columns namespace.
All columns have access to the following customization methods:
Column::make($name)
->action($action) // Set Livewire action that should be called when this column is clicked. The current record key will be passed in as a parameter.
->getValueUsing($callback = fn ($record) => $record->getAttribute('{column name}')) // Set the callback used to retrieve the value of the column from a given record.
->label($label) // Set custom label text for with the column header, which is otherwise automatically generated based on its name. It supports localization strings.
->primary() // Sets the column as primary, which emphasises it and links to access a record.
->searchable() // Allows the values in this column to be searched.
->sortable() // Allows the values in this column to be sorted.
->url($url, $shouldOpenInNewTab = false); // Set URL callback that should be used to generate a URL to send the user to when this column is clicked.Since sorting and searching are done by the database,
sortable()andsearchable()will not be fully functional when using a customgetValueUsing()callback.
Displaying Relationship Data
You set up columns that display results from a related model using dot syntax in its name:
Column::make('customer.name');This would check for a customer relationship on the parent model and output the related customer's name.
Calling Actions
You may want something to happen when a cell is clicked. Usually, this is opening a URL, or running a custom Livewire action.
To open a URL when a cell is clicked, a callback is used to generate the destination. For example:
Column::make('website')
->url(fn ($record) => $record->website, true);Cells of the above column will display the contents of the record's website, and redirect the user to it when they click. The second parameter to url(), true, means that the website will open in a new tab when clicked.
Alternatively, you may specify a custom Livewire action that should run when the column is clicked. The primary key of the clicked record will be passed as a parameter to the action:
Column::make('username')
->action('editUsername');Cells of the above column will call the editUsername() Livewire action when clicked.
Boolean
The trueIcon() and falseIcon() methods support the name of any Blade icon component, and passes a set of formatting classes to it. By default, the Blade Heroicons package is installed, so you may use the name of any Heroicon out of the box. However, you may create your own custom icon components or install an alternative library if you wish.
Boolean::make($name)
->falseIcon($icon = 'heroicon-o-x-circle') // Set the icon that should be displayed when the cell is false.
->trueIcon($icon = 'heroicon-s-check-circle'); // Set the icon that should be displayed when the cell is true.Icon
The options() method supports the names of any Blade icon components, and passes a set of formatting classes to them. By default, the Blade Heroicons package is installed, so you may use the name of any Heroicon out of the box. However, you may create your own custom icon components or install an alternative library if you wish.
Icon::make($name)
->options($options = []); // Set the icon that should be displayed when the cell is a given value.Here is an example usage of this column:
Icon::make('status')
->options([
'heroicon-s-check-circle' => fn ($status) => $status === 'accepted', // When the `status` is `accepted`, render the `check-circle` Heroicon.
'heroicon-s-x-circle' => fn ($status) => $status === 'declined', // When the `status` is `declined`, render the `x-circle` Heroicon.
'heroicon-s-clock' => fn ($status) => $status === 'pending', // When the `status` is `pending`, render the `clock` Heroicon.
]);Image
Image::make($name)
->disk($disk) // Set a custom disk that images should be read from.
->height($height = 40) // Set the height of the image in pixels.
->rounded() // Make the image preview fully rounded.
->size($size) // Set the height and width of the image in pixels.
->width($width); // Set the width of the image in pixels.Text
Text::make($name)
->currency($symbol = '€', $decimalSeparator = '.', $thousandsSeparator = ',', $decimals = 2) // Format values in this column in a currency format.
->date($format = 'F j, Y') // Format values in this column as dates, using PHP date formatting tokens.
->dateTime($format = 'F j, Y H:i:s') // Format values in this column as date-times, using PHP date formatting tokens.
->default() // Set the default value for when this field does not exist.
->formatUsing($callback = fn ($value) => $value) // Set the callback used to format the value of the column.
->limit($limit) // Truncate the value of this column to a certain number of characters.
->options($options = []); // Set the key-value array of available values that this column could hold.Other column types are coming soon.
Developing Custom Column Types
To create a new column type, which may be used in any table, you may generate a class and cell view using:
php artisan make:filament-column Avatar --resourceAlternatively, simple custom columns may be created using a View component, and passing the name of a cell $view in your app:
Columns\View::make($view)
->data($data = []); // Set the key-value array of available data that the view has access to.Filters
Filters are used to scope results in the table. Here is an example of a filter at allows only customers with a type of individual to be shown in the table:
Filter::make('individuals', fn ($query) => $query->where('type', 'individual'));They have access to the following customization options:
Filter::make($name, $callback = fn ($query) => $query)
->label($label); // Set custom label text for with the filter, which is otherwise automatically generated based on its name. It supports localization strings.You can define a default filter to be applied when no other filter is selected by the user by calling defaultFilter() on the Table object:
public static function table(Table $table)
{
return $table
->columns([
// ...
])
->filters([
Filter::make('individuals', fn ($query) => $query->where('type', 'individual')),
Filter::make('organizations', fn ($query) => $query->where('type', 'organization')),
Filter::make('active', fn ($query) => $query->where('is_active', true)),
])
->defaultFilter('individuals');
}Reusable Filters
You may wish to create a filter that you may reuse across multiple tables.
To create a reusable filter, you may use the following command:
php artisan make:filament-filter ActiveFilter --resourceThis will create a new filter in the app/Filament/Resources/Tables/Filters directory:
<?php
namespace App\Filament\Resources\Tables\Filters;
use Filament\Tables\Filter;
class ActiveFilter extends Filter
{
protected function setUp()
{
$this->name('active');
}
public function apply($query)
{
return $query;
}
}You may modify the filter's query in the apply() method of that class:
public function apply($query)
{
return $query->where('is_active', true);
}Currently, filters are static and only one may be applied at a time. Parameter-based filters and support for applying multiple filters at once is coming soon.
Context Customization
You may customize tables based on the page they are used. To do this, you can chain the only() or except() methods onto any column or filter.
use App\Filament\Resources\CustomerResource\Pages;
use Filament\Resources\Tables\Filter;
use Filament\Resources\Tables\Table;
public static function table(Table $table)
{
return $table
->filters([
Filter::make('individuals', fn ($customer) => $customer->type === 'individual')
->only(Pages\ListCustomers::class),
]);
}In this example, the individuals filter will only() be available on the ListCustomers page.
use App\Filament\Resources\CustomerResource\Pages;
use Filament\Resources\Tables\Columns;
use Filament\Resources\Tables\Table;
public static function table(Table $table)
{
return $table
->columns([
Columns\Text::make('name')
->except(Pages\ListCustomers::class, fn ($column) => $column->primary()),
]);
}In this example, the name column will be primary, except() on the ListCustomers page.
This is an incredibly powerful pattern, and allows you to completely customize a table contextually by chaining as many methods as you wish to the callback.
File: docs/05-pages.md
title: Custom Pages
Filament allows you to create completely custom pages for the admin panel.
To create a new page, you can use:
php artisan make:filament-page SettingsThis command will create two files - a page class in the /Pages directory of the Filament directory, and a view in the /pages directory of the Filament views directory.
Page classes are essentially Laravel Livewire components with custom integration utilities for use with Filament.
Authorization
You may create roles for users of Filament that allow them to access specific pages. You may create a Manager role using:
php artisan make:filament-role ManagerAdministrators will now be able to assign this role to any Filament user using the admin panel.
To only allow users with the Manager role to access this page, declare so in the static authorization() method:
use App\Filament\Roles;
public static function authorization()
{
return [
Roles\Manager::allow(),
];
}You may authorize as many roles as you wish.
Please note: administrators will always have full access to every page in your admin panel.
You may want to only deny users with the Manager role from accessing this page. To do this, you may use the static deny() method instead:
use App\Filament\Roles;
public static function authorization()
{
return [
Roles\Manager::deny(),
];
}Customization
Filament will automatically generate a title, navigation label and URL (slug) for your page based on its name. You may override it using static properties of your page class:
public static $label = 'Custom Navigation Label';
public static $slug = 'custom-url-slug';
public static $title = 'Custom Page Title';File: docs/06-dashboard.md
title: Dashboard
Filament allows you to build dynamic custom dashboard widgets very easily. To get started building a Stats widget:
php artisan make:filament-widget StatsThis command will create two files - a widget class in the /Widgets directory of the Filament directory, and a view in the /widgets directory of the Filament views directory.
Widgets are pure Laravel Livewire components, so may use any features of that package.
Pre-built widget templates are coming soon.
Disabling the Default Widgets
By default, two widgets are displayed on the dashboard. These widgets can be disabled by updating the widgets section of the configuration file. Updating each entries to false will remove the corresponding default widget from the dashboard.
'widgets' => [
// ...
'default' => [
'account' => false, // Disables the account widget.
'info' => false, // Disables the info widget.
],
],File: docs/07-navigation.md
title: Navigation
By default, Filament will register navigation items for each of your resources and custom pages. These classes contain static properties that you can override, to configure that navigation item and its order:
public static $icon = 'heroicon-o-document-text';
public static $navigationLabel = 'Custom Navigation Label';
public static $navigationSort = 3;The $icon supports the name of any Blade component, and passes a set of formatting classes to it. By default, the Blade Heroicons package is installed, so you may use the name of any Heroicon out of the box. However, you may create your own custom icon components or install an alternative library if you wish.
Alternatively, you may completely override the static navigationItems() method on the class and register as many custom navigation items as you require:
use Filament\NavigationItem;
public static function navigationItems()
{
return [
NavigationItem::make($label, $url)
->activeRule($activeRule)
->icon($icon = 'heroicon-o-document-text')
->sort($sort = 0),
];
}File: docs/08-theming.md
title: Theming
Filament makes it incredibly simple to customise the look and feel of the panel through "themes".
To create your first theme, run the following command:
php artisan make:filament-theme name-of-themeThis command will create a new file in resources/css/filament called name-of-theme.css.
All of the colors used by Filament can be customised using CSS variables. This means you can change a color in a single place and it will be used throughout the entire panel.
Filament uses 6 different colors:
primarysuccessdangergraybluewhite
The default theme stylesheet should look like this:
:root {
/*
--f-primary-100: #;
--f-primary-200: #;
--f-primary-300: #;
--f-primary-400: #;
--f-primary-500: #;
--f-primary-600: #;
--f-primary-700: #;
--f-primary-800: #;
--f-primary-900: #;
--f-success-100: #;
--f-success-200: #;
--f-success-300: #;
--f-success-400: #;
--f-success-500: #;
--f-success-600: #;
--f-success-700: #;
--f-success-800: #;
--f-success-900: #;
--f-danger-100: #;
--f-danger-200: #;
--f-danger-300: #;
--f-danger-400: #;
--f-danger-500: #;
--f-danger-600: #;
--f-danger-700: #;
--f-danger-800: #;
--f-danger-900: #;
--f-gray-100: #;
--f-gray-200: #;
--f-gray-300: #;
--f-gray-400: #;
--f-gray-500: #;
--f-gray-600: #;
--f-gray-700: #;
--f-gray-800: #;
--f-gray-900: #;
--f-blue-100: #;
--f-blue-200: #;
--f-blue-300: #;
--f-blue-400: #;
--f-blue-500: #;
--f-blue-600: #;
--f-blue-700: #;
--f-blue-800: #;
--f-blue-900: #;
--f-white: #;
*/
}Filament uses Tailwind CSS for styling, therefore each color has 9 different scales.
To customise a color, uncomment the appropriate line in the CSS file and replace the # placeholder with any valid CSS color (hex, RGB, HSL, etc):
:root {
--f-primary-600: #339E8B;
}Registering a Theme
Once you've created your theme, you should register it using the Filament::serving and Filament::registerStyle methods inside the boot method of a service provider:
use Filament\Filament;
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
Filament::serving(function () {
Filament::registerStyle('my-custom-theme', resource_path('css/filament/name-of-theme.css'));
});
}
}Wrapping your style, script and script data related calls in
Filament::servingensures that they will only be run when Filament is being used.
File: docs/09-plugin-development.md
title: Plugin Development
Plugins can be used to extend Filament's default behaviour and create reusable modules for use in multiple applications.
To create a new plugin, extend the Filament\PluginServiceProvider class provided by Filament:
use Filament\PluginServiceProvider;
class ExampleServiceProvider extends PluginServiceProvider
{
// ...
}Registering Plugins
Application Plugins
If you're developing a plugin for a specific application, you should register the new service provider in your config/app.php file:
return [
'providers' => [
// ...
\App\Providers\ExampleServiceProvider::class,
]
];Laravel will load your service provider when bootstrapping and your plugin will be initialised.
Distributed Plugins
Much like a normal Laravel package, you should add your service provider's fully qualified class name to the extra.laravel.providers array in your package's composer.json file:
{
"extra": {
"laravel": {
"providers": [
"Vendor\\Package\\ExampleServiceProvider"
]
}
}
}This will ensure your service provider is automatically loaded by Laravel when the package is installed.
Resources
To register a custom resource, add the fully qualified class name to the protected $resources array in your service provider.
use Vendor\Package\Resources\CustomResource;
class ExampleServiceProvider extends PluginServiceProvider
{
protected $resources = [
CustomResource::class,
];
}Filament will automatically register your Resource and ensure that Livewire can discover it.
Pages
To register a custom page, add the fully qualified class name to the protected $pages array in your service provider.
use Vendor\Package\Pages\CustomPage;
class ExampleServiceProvider extends PluginServiceProvider
{
protected $pages = [
CustomPage::class,
];
}Filament will automatically register your Page and ensure that Livewire can discover it.
Widgets
To register a custom widget, add the fully qualified class name to the protected $widgets array in your service provider.
use Vendor\Package\Widgers\CustomWidget;
class ExampleServiceProvider extends PluginServiceProvider
{
protected $widgets = [
CustomWidget::class,
];
}Filament will automatically register your Widget and ensure that Livewire can discover it.
Roles
To register a custom role, add the fully qualified class name to the protected $roles array in your service provider.
use Vendor\Package\Roles\CustomRole;
class ExampleServiceProvider extends PluginServiceProvider
{
protected $roles = [
CustomRole::class,
];
}Filament will automatically register your Role and ensure it's available for use throughout your application.
Frontend Assets
Filament plugins can also register their own frontend assets. These assets will be included on all Filament related pages, allowing you to use your own CSS and JavaScript.
Stylesheets
To include a custom stylesheet, add it to the protected $styles property in your service provider. You should use a unique name as the key and the URL to the stylesheet as the value.
class ExampleServiceProvider extends PluginServiceProvider
{
protected $styles = [
'my-package-styles' => '/vendor/my-package/css/style.css',
];
}If you need to dynamically generate the key or value, you can overwrite the protected styles() method and return an array of key/value pairs, just like the $styles property:
class ExampleServiceProvider extends PluginServiceProvider
{
protected function styles()
{
return [
'my-package-styles' => asset('/vendor/my-package/css/style.css'),
];
}
}Scripts
To include a custom script, add it to the protected $scripts property in your service provider. You should use a unique name as the key and the URL to the script as the value.
class ExampleServiceProvider extends PluginServiceProvider
{
protected $scripts = [
'my-package-scripts' => '/vendor/my-package/js/main.js'
];
}If you need to dynamically generate the key or value, you can overwrite the protected scripts() method and return an array of key/value pairs, just like the $scripts property:
class ExampleServiceProvider extends PluginServiceProvider
{
protected function scripts()
{
return [
'my-package-scripts' => asset('/vendor/my-package/js/main.js'),
];
}
}Providing Data to the Frontend
Whilst building your plugin, you might find the need to generate some data on the server and access it on the client.
To do this, add a new protected function scriptData() to your service provider and return an array of string keys and values that can be passed to converted into JSON.
class ExampleServiceProvider extends PluginServiceProvider
{
protected function scriptData()
{
return [
'user' => Auth::user(),
];
}
}Filament uses the
@jsonBlade directive to convert your script data into a valid JavaScript object. You can find out more about this directive in the official Laravel documentation.