Preserve Search Filters During Laravel Pagination
Build an article list with title search, stable ordering, and pagination links that preserve the query.
You are reading a translated version.
A reader searches for an article, opens page two, and loses the filter. A common cause is pagination links that omit the search parameter.
This example uses a q title filter and includes only published articles. Build the query before calling paginate. Adapt the view and route names to your application.
Implementation example
public function index(\Illuminate\Http\Request $request)
{
$data = $request->validate(['q' => ['nullable', 'string', 'max:100']]);
$query = trim($data['q'] ?? '');
$posts = \App\Models\Post::query()
->whereNotNull('published_at')
->where('published_at', '<=', now())
->when($query !== '', fn ($builder) =>
$builder->where('title', 'like', '%'.$query.'%'))
->orderByDesc('published_at')
->orderByDesc('id')
->paginate(10)
->appends(['q' => $query]);
return view('articles.index', compact('posts', 'query'));
}
<form method="get">
<label for="query">Search articles</label>
<input id="query" name="q" value="{{ $query }}">
<button>Search</button>
</form>
@foreach ($posts as $post)
<h2>{{ $post->title }}</h2>
@endforeach
{{ $posts->links() }}
Check the result
Create more than ten articles matching the keyword. Open page two and confirm the URL retains q. Submit a new search: because the form has no page field, it starts on page one.
The secondary ID ordering resolves equal publication timestamps. This example uses SQL LIKE semantics, so % and _ in input act as wildcards. Add database-appropriate escaping if literal matching is required. For large datasets, evaluate indexing or a search engine instead of assuming %term% will scale.
