- Published on
How to add quick filters (or actions) to your table
- Authors
-
-
- Name
- Cristian Iosif
- Laravel and FilamentPHP Enthusiast
- X.com
- @X
-
Filament PHP is already fantastic when it comes to table customization—but sometimes you need to go beyond the default filters or actions and inject quick filter UIs right above the table, exactly where users expect them.
This tutorial walks through how I added "Departure" and "Destination" city dropdowns above a flights table using:
- Filament's TablesRenderHook
- Livewire components
- Eloquent queries that react to user input
Let’s dive in! 🏊♂️
# Update the ListPage for Your Resource
If you’re using a resource like FlightResource, you probably already have a ListRecords page. Let’s extend it.
1class ListFlights extends ListRecords2{3 protected static string $resource = FlightResource::class;4 5 public ?int $departureCityId = null;6 public ?int $destinationCityId = null;7 8 // ...9}
We define two public properties to hold the selected city filters.
# Register Custom UI Elements using Render Hooks
Filament provides several "render hooks" to inject HTML into specific parts of the UI. For our case, we’ll hook into TOOLBAR_START to add custom Livewire filters above the table.
1use Filament\Support\Facades\FilamentView; 2use Filament\Tables\View\TablesRenderHook; 3 4public function boot(): void 5{ 6 FilamentView::registerRenderHook( 7 TablesRenderHook::TOOLBAR_START, 8 fn () => Blade::render('@livewire(\'filters.flights.departure\')') 9 );10 11 FilamentView::registerRenderHook(12 TablesRenderHook::TOOLBAR_START,13 fn () => Blade::render('@livewire(\'filters.flights.destination\')')14 );15}
We’re injecting two Livewire components: one for departure, and one for destination.
# Handle Events from Filter Components
To update the table based on the selected filters, we’ll listen to Livewire events.
1//... 2#[On('departureCitySelected')] 3public function handleDepartureCitySelected(?int $departureCityId): void 4{ 5 $this->departureCityId = $departureCityId; 6} 7 8#[On('destinationSelected')] 9public function handleDestinationCitySelected(?int $destinationCityId): void10{11 $this->destinationCityId = $destinationCityId;12}13//...
These event listeners update the state when a filter value is selected in the dropdowns.
# Modify the Table Query
Time to wire everything together by modifying the getTableQuery() method:
1protected function getTableQuery(): ?Builder2{3 return Flight::query()4 ->when($this->departureCityId, fn ($q) => $q->where('departure_city_id', $this->departureCityId))5 ->when($this->destinationCityId, fn ($q) => $q->where('destination_city_id', $this->destinationCityId));6}
Now the table will automatically refresh when you update the filter values.
# Create the Filter Components
Each filter (departure and destination) is a standalone Livewire component using Filament’s form builder.
1// app/Http/Livewire/Filters/Flights/Departure.php 2 3class Departure extends Component implements HasForms 4{ 5 use InteractsWithForms; 6 7 public ?array $data = []; 8 9 public function mount(): void10 {11 $this->form->fill($this->data);12 }13 14 public function form(Form $form): Form15 {16 return $form17 ->schema([18 Forms\Components\Select::make('departure_city_id')19 ->label('')20 ->placeholder('Select Departure')21 ->options(City::query()->pluck('name', 'id')->unique())22 ->afterStateUpdated(fn ($state) => $this->dispatch('departureCitySelected', departureCityId: $state))23 ->live()24 ])25 ->statePath('data');26 }27 28 public function render()29 {30 return <<<'HTML'31 {{ $this->form }}32 HTML;33 }34}35//..36 37 38 39// app/Http/Livewire/Filters/Flights/Destination.php40 41class Destination extends Component implements HasForms42{43 use InteractsWithForms;44 45 public ?array $data = [];46 47 public function form(Form $form): Form48 {49 return $form50 ->schema([51 Forms\Components\Select::make('destination_city_id')52 ->placeholder('Select Destination')53 ->options(City::query()->pluck('name', 'id')->unique())54 ->afterStateUpdated(fn ($state) => $this->dispatch('destinationCitySelected', destinationCityId: $state))55 ->live()56 ])57 ->statePath('data');58 }59 60 public function render()61 {62 return <<<'HTML'63 {{ $this->form }}64 HTML;65 }66}
# Summary
Here's what we did:
- Extended Filament's List page to add reactive filters
- Injected UI using TablesRenderHook
- Created two simple Livewire components for dropdown filters
- Updated the Eloquent query to reflect filter changes
- Used Livewire events for real-time updates
This pattern gives you flexibility to add any kind of quick filter or custom action above your table—whether it’s tags, buttons, status toggles, or search boxes.