Fixing Pagination Breaks in FilamentPHP Tables After Adding Custom Filters
When custom filters break Filament table pagination, the issue often lies in query ordering, eager loading timing, URL parameter handling, or cache keys. This guide walks you through diagnosing the problem, checking the query chain, and applying fixes that restore proper pagination behavior.
12 Jun 2026, 06:02 UTC

Problem & Key Takeaway
When you add a custom filter to a Filament table, the pagination controls can suddenly disappear or the table may list every row on a single page. The root cause is usually the filter altering the query chain or the pagination state. The quick fix is to ensure paginate() is the last call in getTableQuery() and that filter logic doesn’t strip query‑string parameters or force eager loading after pagination.
Recognizable Symptoms
- Pagination controls (next, previous, page numbers) are missing.
- Table shows all records on one page, exhausting memory.
- Clicking a filter resets to page 1 and hides subsequent pages.
- Debugbar shows a query without
LIMIT/OFFSETwhen a filter is active.
Cause & Diagnostic Table
| Cause | Diagnostic Check |
|---|---|
Filter replaces the base query before paginate() | Inspect getTableQuery() – is paginate() called after all filter logic? |
Eager loading applied after paginate() | Check for with() or load() after paginate() in the chain. |
| Filter removes pagination query parameters | Open the URL after applying the filter – are page and per_page present? |
| Cached unpaginated results served | Flush cache or disable caching temporarily and reload the table. |
Ordered Checks & Fixes
- Verify Query Chain Order
Open the table resource class and locate
getTableQuery(). Ensure the method ends with->paginate()and that anywhere,join, orwithcalls precede it.public function getTableQuery(): Builder { return Post::query() ->when($this->filters['status'] ?? null, fn (Builder $q, $value) => $q->where('status', $value)) ->when($this->filters['author'] ?? null, fn (Builder $q, $value) => $q->where('author_id', $value)) ->paginate(); }**Fix** – Move any
paginate()call to the end of the chain if it appears earlier. - Check Eager Loading Timing
If you need to eager‑load relations, do so before pagination:
->with(['comments', 'tags']) ->paginate();**Fix** – Remove any
load()orwith()that occurs afterpaginate(). - Inspect URL Parameters
After applying a filter, view the page URL. Filament passes pagination state via
pageandper_page. If the filter action redirects to a URL that omits these, Filament will reset to page 1.**Fix** – In the filter definition, ensure you return the same query string:
public static function applyFilter(Builder $query, string $value): Builder { return $query->where('status', $value); }Do not use
redirect()inside the filter; let Filament handle the query string. - Validate Cache Behavior
Filament may cache the table query. If a filter changes the query but the cache key doesn’t include the filter value, the cached unpaginated result is returned.
To test, run locally:
php artisan cache:flushReload the table with the filter active. If pagination returns, adjust the cache key in
getTableQuery()to include filter parameters, e.g.,Cache::remember('posts_table_' . $this->filters['status'], 60, fn () => $query->paginate()); - Confirm Pagination Clauses in Query
Use Laravel Debugbar or
dd($query->toSql())to verify that the generated SQL includesLIMITandOFFSETwhen filters are active.**Fix** – If missing, double‑check that
paginate()is still being called and that noget()orall()precedes it.
Escalation Criteria
- If all above checks pass but pagination still fails, the issue may lie in a custom
TableFilterscomponent that manually manipulates the query string. Replace it with Filament’s built‑in filter syntax. - For Filament <3 or Laravel <10, the pagination API differs. Consult the version‑specific docs and adjust the query accordingly.
- If caching is unavoidable, consider using a unique cache tag per filter set and clearing it on filter change.
Practical Verification Checklist
- Reload the table after each change.
- Open the Network tab and confirm the request URL includes
page=2when navigating. - Verify the SQL query in the console shows
LIMIT 10 OFFSET 10for page 2. - Check memory usage – if the table loads all rows, pagination is still broken.
Limitations & Notes
These steps assume Filament 3.x and Laravel 10+. On older stacks, paginate() may be called differently, and cache tags might not exist. Always back up your resource classes before modifying query chains.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.