Livewire Pagination Decision Guide: Built‑In vs. Manual AJAX
Choose the right Livewire pagination strategy: built‑in vs. manual AJAX. A decision guide with constraints, trade‑offs, a comparison table, and concrete code examples to help you implement or validate pagination in your Laravel app.
03 Oct 2026, 20:59 UTC

Decision Context
When a Livewire component displays a large dataset, you need a way to move through pages without reloading the whole page. The two common approaches are:
- Built‑in Livewire pagination –
$this->paginate()and$pageproperty. - Manual AJAX pagination – custom JavaScript (Axios, Fetch, or jQuery) to request pages and update the DOM.
Choose the method that matches your project constraints: speed of implementation, control over query parameters, and client‑side interactivity.
Constraints to Consider
- Data size and performance: Large tables may need server‑side pagination.
- SEO and URL consistency: Pagination links should reflect the current query state.
- Custom filtering or sorting: You might need to pass extra parameters.
- Framework version: Livewire 3+ has improved pagination support.
Comparison Table
| Feature | Built‑In Livewire | Manual AJAX |
|---|---|---|
| Implementation effort | Minimal – just add $page and ->paginate() | High – write JS, handle loading, error states, DOM updates |
| Control over query params | Limited – depends on component logic | Full – you can send any data via AJAX |
| SEO friendliness | Good – uses query strings and rel="next" links | Depends on implementation – you must set meta tags manually |
| State persistence | Automatic – Livewire keeps $page in the URL | Manual – you must sync state yourself |
| Browser history | Automatic – each page change updates the URL | Manual – you need to pushState or similar |
| Performance overhead | Low – server renders only the component fragment | Variable – depends on how you update the DOM |
| Learning curve | Low – built‑in helpers | High – custom AJAX logic |
Trade‑Off Summary
If you need a quick, maintainable solution that preserves URL state and works well with Laravel’s paginate() method, go with the built‑in Livewire pagination. It automatically handles $page, generates proper rel="next" links, and keeps the component reactive.
If your UI demands complex client‑side filtering, dynamic query parameters, or you want to decouple the pagination logic from Livewire’s component lifecycle, implement manual AJAX. This gives you full control but increases boilerplate and potential bugs.
Concrete Implementation
1. Built‑In Pagination Example
Assume you have a Post model and want to display posts in pages.
// app/Http/Livewire/PostList.php
namespace App\Http\Livewire;
use Livewire\Component;
use Livewire\WithPagination;
use App\Models\Post;
class PostList extends Component
{
use WithPagination;
// Optional: set per-page count
public int $perPage = 10;
public function render()
{
$posts = Post::orderBy('created_at', 'desc')
->paginate($this->perPage);
return view('livewire.post-list', [
'posts' => $posts,
]);
}
}
{{-- resources/views/livewire/post-list.blade.php --}}
@if ($posts->count())
@foreach ($posts as $post)
- {{ $post->title }}
@endforeach
{{ $posts->links() }}
@else
No posts found.
@endif
Livewire automatically listens for pagination link clicks, updates the $page property, and re-renders the component. No $refresh() call is needed unless you modify the query elsewhere.
2. Manual AJAX Pagination Example
Suppose you want to add a search box that filters posts without reloading the component.
// app/Http/Livewire/AdvancedPostList.php
namespace App\Http\Livewire;
use Livewire\Component;
use App\Models\Post;
class AdvancedPostList extends Component
{
public string $search = '';
public int $currentPage = 1;
public function render()
{
// No pagination here – the view will be refreshed via AJAX
return view('livewire.advanced-post-list');
}
}
{{-- resources/views/livewire/advanced-post-list.blade.php --}}
In the controller, return an HTML fragment:
// routes/web.php
Route::get('/posts/paginate', function (Request $request) {
$perPage = 10;
$posts = App\Models\Post::where('title', 'like', "%{$request->search}%")
->orderBy('created_at', 'desc')
->paginate($perPage);
$html = view('partials.post-list', ['posts' => $posts])->render();
return response()->json(['html' => $html]);
});
This approach gives you full control, but you must manage state, URLs, and potential caching issues yourself.
Validation Checklist
- Navigate to the page containing the Livewire component.
- Click a pagination link; the list should update without a full page reload.
- Inspect the URL – it should contain
?page=2or similar. - Open the Network tab; the request should be an AJAX call to
/livewire/message/{component}(built‑in) or your custom endpoint (manual). - Verify that the
$pageproperty in the component reflects the current page (usedump($this->page)in the render method temporarily).
Limitations & Tips
- Built‑in pagination can become sluggish if the query uses eager loading on many relationships. Consider
lazyloading orselect(...)to reduce payload. - Manual AJAX pagination is more flexible but requires careful handling of CSRF tokens and session state.
- Always test pagination with a realistic dataset to catch performance bottlenecks.
Conclusion
For most Laravel + Livewire projects, the built‑in pagination is the fastest and most reliable path. Use manual AJAX only when you need custom query logic or advanced client‑side interactions that the built‑in helpers cannot accommodate.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.