The List Page
The List page displays a table of all records. It’s defined in the ListCustomers.php page class:
packages/panels/src/Resources/Pages/ListRecords.php
class ListRecords extends Page implements Tables\Contracts\HasTable
{
use Tables\Concerns\InteractsWithTable;
public function table(Table $table): Table
{
return $table;
}
}
Table Configuration
Tables are configured in your resource’s table() method:
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
use Filament\Actions\EditAction;
use Filament\Actions\DeleteBulkAction;
public static function table(Table $table): Table
{
return $table
->columns([
TextColumn::make('name')
->searchable()
->sortable(),
TextColumn::make('email')
->searchable(),
TextColumn::make('created_at')
->dateTime()
->sortable(),
])
->recordActions([
EditAction::make(),
])
->toolbarActions([
BulkActionGroup::make([
DeleteBulkAction::make(),
]),
]);
}
Tabs
Add tabs above the table to filter records:
packages/panels/src/Resources/Pages/ListRecords.php
use Filament\Schemas\Components\Tabs\Tab;
use Illuminate\Database\Eloquent\Builder;
public function getTabs(): array
{
return [
'all' => Tab::make('All customers'),
'active' => Tab::make('Active customers')
->modifyQueryUsing(fn (Builder $query) => $query->where('active', true)),
'inactive' => Tab::make('Inactive customers')
->modifyQueryUsing(fn (Builder $query) => $query->where('active', false)),
];
}
Tab Badges
Add count badges to tabs:
Tab::make('Active')
->modifyQueryUsing(fn (Builder $query) => $query->where('active', true))
->badge(Customer::query()->where('active', true)->count())
->badgeColor('success')
Deferred Badge Loading
For expensive queries, load badges asynchronously:
Tab::make('All customers')
->badge(static fn (): int => Customer::query()->count())
->deferBadge()
The badge() value must be returned from a function when using deferBadge(). Raw values like badge(Customer::query()->count()) will execute immediately.
Tab Icons
use Filament\Support\Enums\IconPosition;
Tab::make('Active')
->icon('heroicon-m-check-circle')
->iconPosition(IconPosition::After)
Default Active Tab
public function getDefaultActiveTab(): string | int | null
{
return 'active';
}
Excluding Tab Queries When Resolving Records
When a record’s state changes, you may want actions to still work:
Tab::make('active')
->modifyQueryUsing(fn (Builder $query) => $query->where('active', true))
->excludeQueryWhenResolvingRecord()
Do not use excludeQueryWhenResolvingRecord() on tabs that enforce authorization rules (tenant filtering, user ownership, etc.).
Customizing the Query
Modify the table query for the List page:
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
public static function table(Table $table): Table
{
return $table
->modifyQueryUsing(fn (Builder $query) => $query->withoutGlobalScopes())
->columns([
// ...
]);
}
Or customize it only on the List page:
use Illuminate\Database\Eloquent\Builder;
protected function modifyQueryWithActiveTab(Builder $query): Builder
{
return $query->latest();
}
Table Features
Search
Sorting
Filters
Actions
Make columns searchable:TextColumn::make('name')
->searchable()
->searchable(['first_name', 'last_name'])
Allow column sorting:TextColumn::make('created_at')
->sortable()
->sortable(['created_at', 'updated_at'])
Add filters to narrow results:use Filament\Tables\Filters\Filter;
$table->filters([
Filter::make('verified')
->query(fn (Builder $query): Builder =>
$query->whereNotNull('email_verified_at')
),
])
Add row-level actions:use Filament\Actions\EditAction;
use Filament\Actions\ViewAction;
use Filament\Actions\DeleteAction;
$table->recordActions([
ViewAction::make(),
EditAction::make(),
DeleteAction::make(),
])
Bulk Actions
Actions that operate on multiple selected records:
use Filament\Actions\BulkActionGroup;
use Filament\Actions\DeleteBulkAction;
use Filament\Actions\Action;
$table->toolbarActions([
BulkActionGroup::make([
DeleteBulkAction::make(),
Action::make('markAsActive')
->action(function (Collection $records) {
$records->each->update(['active' => true]);
}),
]),
])
Record URLs
By default, clicking a row opens the Edit or View page. The List page automatically determines which action is available:
packages/panels/src/Resources/Pages/ListRecords.php
$table->recordUrl(function (Model $record, Table $table): ?string {
foreach (['view', 'edit'] as $action) {
$action = $table->getAction($action);
if (! $action || $action->isHidden()) {
continue;
}
$url = $action->getUrl();
if ($url) {
return $url;
}
}
return null;
})
Customize this behavior:
public static function table(Table $table): Table
{
return $table
->recordUrl(fn (Customer $record) =>
CustomerResource::getUrl('view', ['record' => $record])
)
->columns([
// ...
]);
}
Reordering Records
Allow drag-and-drop reordering:
use Illuminate\Database\Eloquent\Model;
public static function table(Table $table): Table
{
return $table
->reorderable('sort_order')
->columns([
// ...
]);
}
Authorize reordering with a policy method:
public function reorder(User $user): bool
{
return $user->can('reorder customers');
}
Custom Page Content
The List page schema contains these components by default:
packages/panels/src/Resources/Pages/ListRecords.php
use Filament\Schemas\Components\EmbeddedTable;
use Filament\Schemas\Schema;
public function content(Schema $schema): Schema
{
return $schema
->components([
$this->getTabsContentComponent(),
EmbeddedTable::make(),
]);
}
Add custom components:
use Filament\Schemas\Components\Section;
use Filament\Schemas\Schema;
public function content(Schema $schema): Schema
{
return $schema
->components([
Section::make('Customer Overview')
->description('Manage your customer database')
->schema([
// Custom widgets or components
]),
$this->getTabsContentComponent(),
EmbeddedTable::make(),
]);
}
Add actions to the page header:
use Filament\Actions;
protected function getHeaderActions(): array
{
return [
Actions\CreateAction::make(),
Actions\ImportAction::make()
->importer(CustomerImporter::class),
];
}
Empty State
Customize what shows when there are no records:
public static function table(Table $table): Table
{
return $table
->emptyStateHeading('No customers yet')
->emptyStateDescription('Create your first customer to get started.')
->emptyStateIcon('heroicon-o-user-group')
->emptyStateActions([
CreateAction::make(),
])
->columns([
// ...
]);
}
Polling
Auto-refresh the table at intervals:
public static function table(Table $table): Table
{
return $table
->poll('10s')
->columns([
// ...
]);
}
Authorization
Users can access the List page if the viewAny() method of the model policy returns true:
public function viewAny(User $user): bool
{
return $user->can('view customers');
}
The reorder() method controls record reordering:
public function reorder(User $user): bool
{
return $user->can('reorder customers');
}