Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Filament Table Filter Presets Laravel Package

wotz/filament-table-filter-presets

A Filament plugin for saving, naming, and reapplying table filter combinations as reusable presets, helping admins skip repetitive filtering and switch between common views faster.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Install the package

    composer require wotz/filament-table-filter-presets
    

    Then check the package service provider for publishable assets. At minimum you will likely need the migration:

    php artisan vendor:publish --tag="filament-table-filter-presets-migrations"
    php artisan migrate
    

    Also inspect for a config publish tag (--tag="filament-table-filter-presets-config") before assuming defaults.

  2. Look here first

    • src/Concerns/HasSavedTableFilters.php — the public API you will actually call and override.
    • database/migrations/* — confirms the storage model (presumably a table_filter_presets table keyed by user, resource/table, and name).
    • config/filament-table-filter-presets.php, if present — user model, table name, and authorization hooks.
    • The package’s own tests, if any, for the expected $tableFilters payload shape.
  3. Add the trait to the List page

    use Wotz\FilamentTableFilterPresets\Concerns\HasSavedTableFilters;
    
    class ListTickets extends ListRecords
    {
        use HasSavedTableFilters;
    }
    
  4. Add a filter worth saving The package is designed around Filament table filters, especially query-builder-style filters. A typical first use case:

    public function table(Table $table): Table
    {
        return $table
            ->columns([...])
            ->filters([
                QueryBuilder::make()
                    ->constraints([
                        // your resource constraints
                    ]),
            ]);
    }
    

    Then a user builds “Open tickets assigned to me, older than 3 days”, saves it as a named preset, and reloads it later without rebuilding the filter.

Implementation Patterns

  • Trait-on-the-List-page pattern. The trait belongs on the ListRecords page, not on the Resource or the model. Each List page gets its own preset namespace automatically via the resource/table identity. If you have multiple List pages for the same model (e.g. ListOpenTickets and ListAllTickets), verify whether presets are scoped per page or per resource — this affects whether a preset shows up where you expect.

  • Save-current-state pattern. The normal workflow is: user configures filters in the table UI, triggers “Save preset”, supplies a name, and the package serializes the current $tableFilters state. When loaded, it restores that array and the table re-filters. In custom actions, treat $this->tableFilters as the source of truth:

    // Inspect what will be saved
    dump($this->tableFilters);
    
  • Programmatic apply pattern. If the trait exposes an apply method, prefer it over manually assigning $this->tableFilters, because it will also refresh the filters form and table state. If you must assign manually, follow with the same refresh/reset call the trait uses internally; otherwise the visible filter form and the actual query can drift apart.

  • Per-user scoping. Presets are described as per-user. Rely on auth()->user() resolution rather than passing a user ID from the client. For team/tenant setups, override the trait method that resolves the owning user or add a global scope; do not assume the package is tenant-aware out of the box.

  • Integration with existing table state. Presets compose with pagination, search, sorting, and column toggles, but they should generally only capture filters. Avoid treating a preset as a full “saved view” unless you confirm the package stores more than $tableFilters. If you need saved search/sort too, add that on top rather than expecting it here.

  • Default filters pattern. If your table has ->default() filters, decide whether a loaded preset should replace or merge with them. The safest UX is replace: the preset is the complete filter state. Test this explicitly, since default filters can otherwise reappear after loading a preset.

  • Testing pattern. Use Filament/Livewire table tests to exercise save/load:

    livewire(ListTickets::class)
        ->set('tableFilters.status.value', 'open')
        ->callTableAction(SaveFilterPresetAction::class, data: ['name' => 'My open tickets'])
        ->assertHasNoTableActionErrors();
    

    Then assert the persisted row and reload the page to confirm the preset is offered. Because adoption is low, write these tests against your installed version rather than trusting examples online.

  • Extension pattern. Keep custom behavior in your own List page or a subclassed trait method, not by editing vendor files. Good extension points are the preset model (if configurable), the authorization method, and the save/apply action classes, if the package exposes them.

Gotchas and Tips

  • Very low adoption (0 stars, 0 dependents). Budget time to read the source. There is little community troubleshooting, so pin the exact version in composer.json and review changelogs before upgrading. The 2026 release date also means you should verify compatibility with your installed Filament major version rather than assuming it.

  • Filament version coupling. The package depends on internal Filament table APIs ($tableFilters, filter form state, query builder serialization). Minor Filament upgrades can change the shape of query-builder filter state. After upgrading Filament, re-test saving and loading presets, not just rendering the table.

  • Stale presets after filter changes. If you rename a filter key, remove a filter class, or change query-builder constraints, previously saved presets may reference filters that no longer exist. Loading them can silently do nothing or throw. Consider a migration/cleanup strategy, or validate preset payloads on load and discard unknown keys.

  • Migration not published. The most common first-run failure will be a missing presets table. If saving appears to succeed but nothing persists, check that the package migration was published and run, and that the configured connection is correct.

  • Authorization and IDOR. Presets are likely loaded by ID. Ensure the package (or your override) scopes reads to the current user. Do not expose a generic “load preset by ID” action without an ownership check, especially in multi-tenant panels.

  • Confusion with Filament’s built-in filter persistence. Filament can persist filters in the session or query string. This package is for named, reusable presets. Using both is fine, but document the difference for users; otherwise “why did my filters come back?” and “why did my preset not load?” become the same bug report.

  • Filter form desync. If you apply a preset by assigning $this->tableFilters directly and skip the form refresh, the table query changes but the filter UI still shows the old values. Always use the trait’s apply path or mirror its internal refresh call.

  • Hidden/inactive filters. Filters that are conditionally hidden may still exist in the saved payload. On load, confirm the package re-evaluates visibility and does not apply constraints for filters the user cannot currently see.

  • Debugging tip. Log or dump the raw preset payload ($preset->filters or equivalent) next to $this->tableFilters at save and load time. Most issues are a shape mismatch between the serialized filter state and what Filament expects on rehydration, and comparing those two values identifies it quickly.

  • Default to a replace, not merge, UX. Merging loaded presets with existing filters produces surprising results and is hard to explain. If the package merges, override that behavior or reset filters before applying a preset.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib