Product Decisions This Supports
- Feature scoping for the "power user" table experience. This package is the cheapest way to test the hypothesis that filter discoverability — not filtering power — is the adoption blocker in our admin tables. Our Filament tables already support filters; they live behind a dropdown and users don't find them. Header-level affordances are a well-understood convention (Excel, Airtable, Notion), so this is a UX bet with a familiar mental model rather than a novel interaction users must learn.
- Build vs. buy triage. Estimate the in-house alternative first: a header action + modal + four filter-type form schemas + state sync into Filament's
tableFilters array is roughly 3–5 dev days plus ongoing Filament upgrade maintenance. If our estimate lands there, adoption is a "buy" on cost alone — but a conditional buy, gated on the vendor-risk checks in the next section.
- Roadmap sequencing for ops/back-office tooling. If the roadmap has a "make the internal admin usable by non-engineers" theme, this is a shippable increment toward it. It does not require a data model change, a new API, or a migration — which makes it a low-cost candidate for a "can we ship something visible this sprint" slot.
- Reducing support load as a measurable outcome. The pitch to justify the ticket is fewer "how do I find X" messages to the ops team and fewer exports-to-Excel (the export-to-spreadsheet workaround is itself a signal that in-app filtering is failing). Instrument both before/after; those are the metrics that make the retro honest.
- Deciding what not to build. This package deliberately does not do saved views, cross-column boolean logic, aggregation, or shareable filter URLs. If our users need those, this package is a starting point for the header affordance, not the whole feature — and we should scope the follow-on work rather than assume the plugin covers it.
- Reusable pattern for a build-it-ourselves fallback. Even if we don't adopt, the
syncWith() design is the right architecture to copy: header popups read and write the same filter state as the dropdown, so there is one source of truth and one set of filter indicators. That's the pattern we should hold our own implementation to.
When to Consider This Package
Adopt when:
- We are on Filament v3/v4 and the target tables are internal admin/back-office surfaces used repeatedly by the same people — the value compounds with repeat usage, not with one-off browsing.
- Users arrive from spreadsheet workflows and expect per-column filtering as a baseline, not a feature.
- We already have meaningful Filament table filters configured and want a second entry point to them, not a parallel filtering system. The
syncWith() behavior is the key property: state stays unified, indicators keep their remove buttons, and the standard dropdown isn't duplicated.
- We want a small, MIT-licensed, forkable dependency. The surface area is genuinely small — header action, popup, four input types — so the fork cost if the maintainer walks away is low.
- Date-range presets (this week / last 30 days / this year) map to how our users actually think about time windows. That's a surprising amount of value for a dropdown.
Look elsewhere, or build, when:
- Vendor risk is disqualifying for the surface in question. 21 stars, zero dependents, readme + changelog as the only maturity signal, and a single maintainer. That is fine for an internal admin table; it is not fine for anything customer-facing or contractually supported. Do not adopt without a named owner inside engineering and a pinning strategy.
- We need saved segments, shareable/bookmarkable filter state, cross-column AND/OR logic, or aggregate rollups. Those are product features, not plugin features, and the plugin's UI (a per-column popup) is the wrong shape for them.
- Tables are large enough that free-text search on the search-type column would run unindexed against a big table. Verify query plans for each column we expose; a header filter makes it easier for users to trigger expensive queries.
- We need accessibility guarantees (keyboard trap behavior in popovers, screen-reader labeling of the filter icon) or RTL support beyond Hebrew. RTL is claimed and Hebrew translations ship, but there is no evidence of an audit — we should verify with our own accessibility pass before promising it.
- We are not on Filament, or we are on a version the package doesn't explicitly support. Check the composer constraint before it becomes a mid-sprint surprise.
- The team would rather own this outright. At 3–5 dev days, owning it is defensible; the plugin's real value is the decision already made about interaction design, not the code volume.
Adoption gate (do these before committing):
- Read the actual source, not just the README — it's small enough to review in an afternoon.
- Confirm the Filament version constraint matches ours and that the author responds to issues in a reasonable window.
- Pin to an exact version; vendor the fork if the trial goes well.
- Assign an internal owner. Any unowned dependency in an admin panel becomes a mystery bug in six months.
How to Pitch It (Stakeholders)
To executives (one paragraph):
"Our admin tables can already filter, but users don't find the controls — they export to Excel and filter there, which is slow, error-prone, and invisible to us. This is a small, free, MIT-licensed add-on that puts a filter control directly on each table column header, the way spreadsheets do. It's a familiar pattern, so there's no training cost, and it's a configuration change rather than a new feature — no data migration, no new service, no API. The ask is a timeboxed trial on one or two internal tables with before/after numbers on Excel exports and support requests. The main risk is that it's a small open-source project with a single maintainer, which is why we pin the version, review the code ourselves, and confine the trial to internal tooling."
To engineering (the technical case):
- It's a Filament plugin, not a parallel filtering layer. Header popups are backed by real Filament table filters, so they hit the same query, render the same filter indicators with working remove buttons, and don't add a second state model to reason about.
syncWith() is the important part: one filter state, two entry points. Choosing a value in the header updates the regular dropdown and vice versa. No divergence, no "why does the dropdown say something different from the header."
- Four filter types cover most of what we need — search, date range with presets, single/multi select, numeric range — and map onto filter classes we likely already have.
- Cost is
composer require plus per-column registration. Real cost is the review and the accessibility pass, not the integration.
- Risks we're accepting, explicitly: one maintainer, 21 stars, no dependents, no visible CI or test suite. Mitigations: pin the version, read the source before merge, name an internal owner, and keep it off customer-facing surfaces. If it turns out to be load-bearing, the fork is a day of work because the surface area is small.
- Performance caveat: a header filter makes it trivially easy to trigger unindexed free-text searches. Check the columns we expose before we ship it to a table with real row counts.