Laravel Pagination Example: How to Create Custom Pagination Views in Laravel 5+
You can implement custom pagination views in Laravel by publishing the default Blade templates using php artisan vendor:publish --tag=laravel-pagination, modifying the files in resources/views/vendor/pagination, and rendering them via $paginator->links('vendor.pagination.custom').
Creating a custom Laravel pagination example allows you to align navigation components perfectly with your application's design system. Whether you are maintaining a legacy Laravel 5 application or working with the latest Laravel 12 release, the pagination architecture remains consistent, utilizing the Illuminate\Pagination classes and publishable Blade view files located in the laravel/framework repository.
Understanding Laravel's Pagination Architecture
Laravel's pagination system centers around three primary classes that extend AbstractPaginator: LengthAwarePaginator for full pagination with total counts, Paginator for simple "previous/next" navigation, and CursorPaginator for cursor-based pagination. These classes handle URL generation, query string management, and view rendering through the AbstractPaginator::render() method.
The rendering flow follows this path through the framework source:
- Controller calls
Model::paginate(15), returning aLengthAwarePaginatorinstance - The paginator stores a default view name (e.g.,
pagination::tailwind) - Blade calls
$paginator->links(), which invokesAbstractPaginator::render() render()resolves the view factory viastatic::viewFactory()and passes$paginatorand$elementsvariables to the Blade template
Key source files in the laravel/framework repository include:
src/Illuminate/Pagination/PaginationServiceProvider.php– Registers view publishing and the view factory resolversrc/Illuminate/Pagination/AbstractPaginator.php– Containsrender(), URL building, and view resolution logicsrc/Illuminate/Pagination/resources/views/– Default Blade templates includingtailwind.blade.php,default.blade.php, andbootstrap-5.blade.php
Publishing Default Pagination Views
Before creating custom pagination views, you must publish the framework's default views to your application. This copies files from src/Illuminate/Pagination/resources/views to resources/views/vendor/pagination, where they can be safely modified without affecting the core framework.
Execute the following Artisan command:
php artisan vendor:publish --tag=laravel-pagination
This command triggers the publish logic defined in PaginationServiceProvider, which tags the pagination views with laravel-pagination. After running the command, you will find these files in your application:
resources/views/vendor/pagination/default.blade.phpresources/views/vendor/pagination/tailwind.blade.phpresources/views/vendor/pagination/bootstrap-4.blade.phpresources/views/vendor/pagination/bootstrap-5.blade.phpresources/views/vendor/pagination/simple-default.blade.phpresources/views/vendor/pagination/simple-tailwind.blade.php
Creating a Custom Pagination View
After publishing, create a new Blade file in resources/views/vendor/pagination/custom.blade.php. The paginator automatically injects two critical variables: $paginator (the paginator instance with methods like currentPage(), hasPages(), and nextPageUrl()) and $elements (an array containing page numbers and ellipsis strings).
Custom Blade Template Structure
@if ($paginator->hasPages())
<nav aria-label="{{ __('Pagination') }}">
<ul class="my-custom-pagination">
{{-- Previous Page Link --}}
@if ($paginator->onFirstPage())
<li class="disabled" aria-disabled="true"><span>«</span></li>
@else
<li><a href="{{ $paginator->previousPageUrl() }}" rel="prev">«</a></li>
@endif
{{-- Pagination Elements --}}
@foreach ($elements as $element)
{{-- "Three Dots" Separator --}}
@if (is_string($element))
<li class="disabled"><span>{{ $element }}</span></li>
@endif
{{-- Array Of Links --}}
@if (is_array($element))
@foreach ($element as $page => $url)
@if ($page == $paginator->currentPage())
<li class="active" aria-current="page"><span>{{ $page }}</span></li>
@else
<li><a href="{{ $url }}">{{ $page }}</a></li>
@endif
@endforeach
@endif
@endforeach
{{-- Next Page Link --}}
@if ($paginator->hasMorePages())
<li><a href="{{ $paginator->nextPageUrl() }}" rel="next">»</a></li>
@else
<li class="disabled" aria-disabled="true"><span>»</span></li>
@endif
</ul>
</nav>
@endif
This template leverages the AbstractPaginator API methods including hasPages(), onFirstPage(), previousPageUrl(), currentPage(), hasMorePages(), and nextPageUrl(). The $elements array structure is generated by the paginator's URL building logic in AbstractPaginator and contains either strings representing ellipses or associative arrays of page numbers and URLs.
Rendering Custom Pagination in Your Application
Once your custom view exists, render it either per-instance via the links() method or set it as the global default for all paginators.
Per-Instance Rendering
In your controller, retrieve paginated data using Eloquent:
use App\Models\User;
public function index()
{
$users = User::paginate(15);
return view('users.index', compact('users'));
}
In your Blade template, specify the custom view as the first argument to links():
@foreach ($users as $user)
<div>{{ $user->name }}</div>
@endforeach
{{ $users->links('vendor.pagination.custom') }}
The links() method in AbstractPaginator accepts an optional view name parameter and forwards it to render(), which resolves the view factory and renders the specified template.
Global Default View Configuration
To use your custom view application-wide without specifying it in every links() call, configure the default in a service provider's boot() method:
use Illuminate\Pagination\Paginator;
public function boot()
{
Paginator::defaultView('vendor.pagination.custom');
// For simple pagination (previous/next only)
Paginator::defaultSimpleView('vendor.pagination.simple-custom');
}
This configuration affects all paginators that call links() without arguments, directing them to use your custom Blade templates instead of the framework defaults defined in PaginationServiceProvider.
Summary
- Laravel pagination utilizes
LengthAwarePaginator,Paginator, andCursorPaginatorclasses extendingAbstractPaginatorfor URL generation and view rendering. - Publish default views using
php artisan vendor:publish --tag=laravel-paginationto copy files fromsrc/Illuminate/Pagination/resources/viewstoresources/views/vendor/pagination. - Custom views receive
$paginatorand$elementsvariables fromAbstractPaginator::render(), providing access to methods likecurrentPage(),hasPages(), andnextPageUrl(). - Render custom views per-instance via
$paginator->links('vendor.pagination.custom')or globally viaPaginator::defaultView()in a service provider. - Key source files include
PaginationServiceProvider.phpfor publishing logic andAbstractPaginator.phpfor the core rendering implementation.
Frequently Asked Questions
Does this Laravel pagination example work for Laravel 5 applications?
Yes, the architectural patterns for custom pagination views have remained consistent since Laravel 5. While the default styling has evolved from Bootstrap (Laravel 5) to Tailwind (modern versions), the process of publishing views via vendor:publish, creating custom Blade files in resources/views/vendor/pagination, and rendering them via links() follows the same API across all versions from Laravel 5 through Laravel 12.
What is the difference between simple pagination and length-aware pagination?
LengthAwarePaginator (returned by Model::paginate()) queries the total record count to calculate the last page number, enabling it to display a full range of page numbers and "Page X of Y" information. SimplePaginator (returned by Model::simplePaginate()) only checks for the existence of a next page without counting total records, making it more memory-efficient for large datasets but limiting navigation to only "Previous" and "Next" links. Both support custom views through the same links() method.
How do I switch between Bootstrap and Tailwind pagination styles?
You can explicitly set the default pagination view in your AppServiceProvider or another service provider's boot() method. For Bootstrap 5, call Paginator::useBootstrapFive() or Paginator::defaultView('pagination::bootstrap-5'). For Tailwind, use Paginator::defaultView('pagination::tailwind'). These view names reference the Blade files located in src/Illuminate/Pagination/resources/views that were published to your resources/views/vendor/pagination directory.
Can I use custom pagination views with API or JSON responses?
No, custom pagination views only apply to HTML responses rendered through Blade templates. When building API endpoints that return JSON, the paginator automatically converts to JSON format using the toArray() or toJson() methods defined in AbstractPaginator. For API resources, use Resource::collection($paginator), which respects the pagination structure but ignores Blade view configurations entirely. Custom views are specifically designed for server-side rendered HTML applications.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →