suenerds/nova-searchable-belongs-to-filter
This Laravel Nova package replaces BelongsTo relationship filter dropdowns with a searchable, typeahead-style input, so you can quickly find and filter by related records even when the underlying table contains thousands of options.
belongsTo filter is a narrow, well-understood piece of custom engineering. Writing the equivalent in-house means an async search endpoint, a Vue component, Nova asset registration, and ongoing upgrade tracking. An MIT dependency of a few hundred lines costs less to adopt and pin than to rebuild — a tactical buy, not a platform commitment.BelongsTo filter renders a <select> containing every row of the related table. On high-cardinality relationships (customers, accounts, owners, regions) that means slow index pages and unusable dropdowns. Type-ahead search cuts time-to-find and mis-selections, measurable in admin clicks and seconds for a real task like "find the post by author Jordan Lee."belongsTo relationships where the related table has thousands of records. If related tables are small, the package delivers nothing and the native filter is correct.Adopt when:
belongsTo filter dropdowns. The value scales with cardinality. Start with the biggest related table, not the most complex resource; blast radius is nil (one line in filters(), one-line revert), so there is no reason to pilot on something trivial.^2.0.3 specifically — earlier tags don't declare Laravel 11 support and Composer will refuse on constraint conflicts. Run composer why-not during adoption.Laravel\Nova\Filters\BelongsTo in the admin runbook.name, email, account_number, external_id) and make sure the searched columns have indexes. A LIKE '%term%' scan on a 500k-row table turns a snappy filter into a timeout.Look elsewhere when:
apply() override — no config file, no events.src/SearchableBelongsToFilter.php and resources/js/components/Filter.vue end-to-end (an afternoon) and treat the source as the spec before signing off.For executives — one paragraph. Our Nova admin panel has a filter that lists every record in a related table as a dropdown. On our largest tables that makes admin index pages slow and the filter practically unusable, which shows up as wasted staff time and wrong-record selections. This is an MIT-licensed, few-hundred-line package that swaps that dropdown for a type-ahead search box. It's a one-line change per resource, it stores the same filter value so bookmarks and existing workflows keep working, and reverting is another one line. Cost is essentially zero; the return is measurable admin time saved on the exact screens where staff currently scroll thousands of rows.
For engineering — the technical summary. A drop-in replacement for Laravel\Nova\Filters\BelongsTo. SearchableBelongsToFilter::make('Author')->filterBy('author')->searchColumns(['name','email']) — note filterBy takes the relationship method name, not the foreign key column. Install is composer require suenerds/nova-searchable-belongs-to-filter followed by php artisan nova:publish; the first failure mode for a filter that renders but appears inert or unstyled is unregistered compiled assets, so hard-refresh and check the network tab before debugging anything else. The stored filter value stays a scalar related-model ID, so the filtered index behaves identically to the old filter and existing filter URLs keep resolving.
What we're asking engineering to own. Pin ^2.0.3 (Laravel 11 support landed there; earlier tags don't declare it). Ship it first on the highest-cardinality relationship. Add an acceptance test that visits a Nova index with the filter applied and asserts expected records appear and an out-of-scope tenant's records do not — authorization flow through the related resource's indexQuery must be verified, not assumed, because the failure mode is cross-tenant data exposure rather than a UX bug. Verify empty state means "no constraint" and that a saved filter value renders the selected label without a search round-trip. Check Filter.vue for a result cap and debounce; if there isn't one, add a take() constraint via apply() override. Run a two-way UAT with ops — same task, old filter vs. new — and put the clicks-and-seconds delta in the release note. Re-test the Nova index page after every Nova or Laravel major bump, and keep the one-line revert documented.
How can I help you explore Laravel packages today?