leek/filament-header-filters
Add inline filters to Filament table column headers. Attach any BaseFilter (selects, date pickers, min/max ranges, custom schemas) as a richer alternative to individual searchable fields. Works with Filament v4/v5, PHP 8.2+.
Group inside the table's own filters form, but Filament's schema renderer wraps every top-level schema component in its own .fi-grid-col grid item, which still occupied a grid row (and a row-gap) per header filter. With several withHeaderFilter() columns, these empty rows pushed the "Apply filters" button down by a large, unexplained gap. New CSS rules — scoped to Filament's filters wrappers across every layout (dropdown, modal, and above/below/before/after-content) — now hide the empty .fi-grid-col wrapper too. No PHP changes, so removeTableFilter() keeps working as fixed in v2.0.23. (#5, thanks @x-adam)Full Changelog: https://github.com/leek/filament-header-filters/compare/v2.0.23...v2.0.24
->default()) could not be dismissed. Two causes: Filament caches the panel filters form in bootedInteractsWithTable() before the header filters are pushed onto the table, so removeTableFilter() looked the filter up in a schema that didn't contain it; and the panel-form groups for header filters were schema-hidden(), which also makes them invisible to getComponentByStatePath(). The panel filters form is now re-cached after header filters register, and their panel groups are CSS-hidden instead of schema-hidden so Filament can find and reset their fields.Full Changelog: https://github.com/leek/filament-header-filters/compare/v2.0.22...v2.0.23
Security and release-hygiene improvements:
run-tests (PHP 8.2–8.4 × Laravel 11–13) and code-style (Pint), with all GitHub Actions pinned to commit SHAsSECURITY.md with private vulnerability reporting instructions.gitattributes export-ignore rules keep tests, CI config, and build tooling out of Composer installsFull Changelog: https://github.com/leek/filament-header-filters/compare/v2.0.21...v2.0.22
Docs / metadata only — no code or behavior changes.
composer.json suggest entries pointing at companion plugins (sorted alphabetically).+N badge to remove hidden selections.Avoid redundant responsive badge overflow class writes so settled recalculation does not mutate already-correct containers.
Avoid redundant responsive badge overflow count writes after the SPA navigation fix, making settled recalculation a no-op when the displayed overflow state is unchanged.
Fix responsive multi-select badge overflow after Filament SPA navigation by reconnecting the shared measurement root and observers after Livewire swaps the document body.
Measure candidate +N widths on cloned badges so recalculation does not mutate the live DOM.
Avoid repeated no-op writes while recalculating responsive overflow badges.
Allow count-only overflow badges when compact header filters do not have room for both a selected label and +N.
Reserve the measured overflow badge width so compact header multi-select filters can display +N without clipping.
Fix responsive overflow measurement by using a real offscreen badge clone so +N appears when header filter badges are clipped.
Fix responsive multi-select header filter overflow stability and prevent the first visible badge from growing to fill the control.
+N count.+N badge width is included during measurement so wider counts do not cause secondary overflow.Header-filter registration was gated by a once-per-request boolean flag, and the filters were pushed onto the table imperatively. When a consumer calls resetTable() — e.g. a page that re-renders the table after an external page filter or action changes — bootedInteractsWithTable() rebuilds the table from scratch and drops those pushed filters. The flag stayed true, so they never re-registered.
The result was a confusing bug: a header filter showed as selected but stopped actually filtering the table.
Registration is now tracked per Table instance via a WeakMap in HeaderFilterRegistry, rather than a component-level boolean. A rebuilt table is a new instance, so registration runs again and the filters are re-applied — while staying idempotent within a single render.
If you added a resetTable() workaround in your own page to force re-registration, you can drop it after upgrading.
Previously, selecting 2+ options in a multi-select header filter stacked a full-width badge per selection, wrapping each long label character-by-character and blowing up the row height.
The filter now collapses to a single line:
min-width: 16rem) when a multi-select carries a selection,+N overflow count.CSS-only — no API or behavior changes. Pull options still toggle individual selections from the dropdown.
hasHeaderFilters() is only called when the method or macro is actually registered, preventing errors on tables that don't use header filters (thanks @luca-alsina, #3).max-content.vendor/leek/filament-header-filters/resources/css/filament-header-filters.css.resources/dist stylesheet and package-local Vite build files.Add the package stylesheet to your Filament panel theme after the Filament theme import, then rebuild your app assets with npm run build.
Vite-built CSS sources (package.json, vite.config.js, resources/css, resources/js), refreshed dist CSS, art update, .gitignore for /.claude, minor pint cleanup of the trait. No API changes.
First stable release.
->headerFilter(BaseFilter $filter) on any table column. Accepts SelectFilter, Filter, and any BaseFilter subclass.tableFilters). Indicators, reset, and session persistence work.deferFilters().composer require leek/filament-header-filters
Add the HasHeaderFilters trait to any Livewire component using InteractsWithTable:
use Leek\FilamentHeaderFilters\Concerns\HasHeaderFilters;
class ListOrders extends ListRecords
{
use HasHeaderFilters;
}
See README for dropdown, min/max, and date range examples.
Free port of filamentphp/filament#19432.
How can I help you explore Laravel packages today?