Skip to main content

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

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(),
        ]);
}

Header Actions

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');
}

Build docs developers (and LLMs) love