leek / filament-header-filters
Inline header filter components for Filament tables — attach any BaseFilter to a column header.
Requires
- php: ^8.2
- filament/filament: ^4.0 || ^5.0
- spatie/laravel-package-tools: ^1.15
Requires (Dev)
- laravel/pint: ^1.0
- orchestra/testbench: ^9.0 || ^10.0 || ^11.0
- pestphp/pest: ^3.0 || ^4.0
- pestphp/pest-plugin-laravel: ^3.0 || ^4.0
README
Inline header filters for Filament tables. Attach any BaseFilter to a column header — select dropdowns, date pickers, min/max ranges, custom multi-field schemas — as a richer alternative to searchable(isIndividual: true).
Screenshot
Requirements
- PHP 8.2+
- Filament v4.x or v5.x
Installation
composer require leek/filament-header-filters
Add the HasHeaderFilters trait to any Livewire component that uses InteractsWithTable (resource list pages, custom Livewire table components):
use Filament\Resources\Pages\ListRecords; use Leek\FilamentHeaderFilters\Concerns\HasHeaderFilters; class ListOrders extends ListRecords { use HasHeaderFilters; }
Styles
If your panel uses a custom Vite theme (->viteTheme(...)), import the plugin CSS into your theme.css so Tailwind's build pipeline picks it up and cascade order is correct:
@import '../../../../vendor/filament/filament/resources/css/theme.css'; @import '../../../../vendor/leek/filament-header-filters/resources/dist/filament-header-filters.css';
Then rebuild: npm run build. If you don't use a custom theme, the plugin's CSS loads automatically via FilamentAsset.
Usage
Call ->headerFilter() on any column and pass a filter instance.
Dropdown filter
Use SelectFilter for exact-match column filtering. The default placeholder is "All".
use App\Enums\OrderStatus; use Filament\Tables\Columns\TextColumn; use Filament\Tables\Filters\SelectFilter; TextColumn::make('status') ->badge() ->headerFilter( SelectFilter::make('status') ->options(OrderStatus::class) ->native(false) )
Min/max range filter
Use a custom Filter with two TextInput fields and ->columns(2) for a side-by-side numeric range:
use Filament\Forms\Components\TextInput; use Filament\Tables\Columns\TextColumn; use Filament\Tables\Filters\Filter; use Illuminate\Database\Eloquent\Builder; TextColumn::make('total_price') ->headerFilter( Filter::make('total_price_range') ->columns(2) ->schema([ TextInput::make('min')->numeric()->placeholder('Min'), TextInput::make('max')->numeric()->placeholder('Max'), ]) ->query(fn (Builder $query, array $data): Builder => $query ->when($data['min'] ?? null, fn (Builder $q, $v): Builder => $q->where('total_price', '>=', $v)) ->when($data['max'] ?? null, fn (Builder $q, $v): Builder => $q->where('total_price', '<=', $v)) ) )
Date range filter
Use DatePicker fields for a two-date range:
use Filament\Forms\Components\DatePicker; use Filament\Tables\Columns\TextColumn; use Filament\Tables\Filters\Filter; use Illuminate\Database\Eloquent\Builder; TextColumn::make('created_at') ->label('Order date') ->date() ->headerFilter( Filter::make('order_date_range') ->columns(2) ->schema([ DatePicker::make('from')->placeholder('From')->native(false), DatePicker::make('until')->placeholder('Until')->native(false), ]) ->query(fn (Builder $query, array $data): Builder => $query ->when($data['from'] ?? null, fn (Builder $q, $v): Builder => $q->whereDate('created_at', '>=', $v)) ->when($data['until'] ?? null, fn (Builder $q, $v): Builder => $q->whereDate('created_at', '<=', $v)) ) )
Behavior
- Header filters share state with panel filters (
$tableFilters). Filter indicators, reset, and session persistence all work. - Header filters are always live — they apply immediately on change, regardless of
deferFilters(). - Field labels inside header filters are auto-hidden; the column header acts as the label.
- Hidden columns' header filters are not applied to the query.
How it works
The package ships:
Column::macro('headerFilter', ...),getHeaderFilter(),hasHeaderFilter()BaseFilter::macro('columnName', ...),getColumnName(),isHeaderFilter()Table::macro('getHeaderFilters', ...),hasHeaderFilters()- A
HasHeaderFiltersLivewire trait that wires filters into the table, seeds header filter state, and exposesgetTableHeaderFiltersForm() - A view override (
filament-tables::index) that renders the header filter row under the column search row - A small CSS file raising the z-index and lifting overflow clipping so dropdown / date picker popups escape the table container
The view override is a patched copy of Filament's table view. If you upgrade Filament and something breaks, please open an issue.
Testing
composer test
License
MIT. See LICENSE.md.
