# Laravel Pagination Example: How to Create Custom Pagination Views in Laravel 5+

> Learn how to create custom pagination views in Laravel 5 with this clear example. Publish, modify, and render your own pagination templates easily.

- Repository: [Laravel/framework](https://github.com/laravel/framework)
- Tags: tutorial
- Published: 2026-02-16

---

**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:

1. Controller calls `Model::paginate(15)`, returning a `LengthAwarePaginator` instance
2. The paginator stores a default view name (e.g., `pagination::tailwind`)
3. Blade calls `$paginator->links()`, which invokes `AbstractPaginator::render()`
4. `render()` resolves the view factory via `static::viewFactory()` and passes `$paginator` and `$elements` variables to the Blade template

Key source files in the `laravel/framework` repository include:
- [`src/Illuminate/Pagination/PaginationServiceProvider.php`](https://github.com/laravel/framework/blob/main/src/Illuminate/Pagination/PaginationServiceProvider.php) – Registers view publishing and the view factory resolver
- [`src/Illuminate/Pagination/AbstractPaginator.php`](https://github.com/laravel/framework/blob/main/src/Illuminate/Pagination/AbstractPaginator.php) – Contains `render()`, URL building, and view resolution logic
- `src/Illuminate/Pagination/resources/views/` – Default Blade templates including [`tailwind.blade.php`](https://github.com/laravel/framework/blob/main/tailwind.blade.php), [`default.blade.php`](https://github.com/laravel/framework/blob/main/default.blade.php), and [`bootstrap-5.blade.php`](https://github.com/laravel/framework/blob/main/bootstrap-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:

```bash
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.php`](https://github.com/laravel/framework/blob/main/resources/views/vendor/pagination/default.blade.php)
- [`resources/views/vendor/pagination/tailwind.blade.php`](https://github.com/laravel/framework/blob/main/resources/views/vendor/pagination/tailwind.blade.php)
- [`resources/views/vendor/pagination/bootstrap-4.blade.php`](https://github.com/laravel/framework/blob/main/resources/views/vendor/pagination/bootstrap-4.blade.php)
- [`resources/views/vendor/pagination/bootstrap-5.blade.php`](https://github.com/laravel/framework/blob/main/resources/views/vendor/pagination/bootstrap-5.blade.php)
- [`resources/views/vendor/pagination/simple-default.blade.php`](https://github.com/laravel/framework/blob/main/resources/views/vendor/pagination/simple-default.blade.php)
- [`resources/views/vendor/pagination/simple-tailwind.blade.php`](https://github.com/laravel/framework/blob/main/resources/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`](https://github.com/laravel/framework/blob/main/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

```blade
@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>&laquo;</span></li>
            @else
                <li><a href="{{ $paginator->previousPageUrl() }}" rel="prev">&laquo;</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">&raquo;</a></li>
            @else
                <li class="disabled" aria-disabled="true"><span>&raquo;</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:

```php
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()`:

```blade
@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:

```php
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`, and `CursorPaginator` classes extending `AbstractPaginator` for URL generation and view rendering.
- **Publish default views** using `php artisan vendor:publish --tag=laravel-pagination` to copy files from `src/Illuminate/Pagination/resources/views` to `resources/views/vendor/pagination`.
- **Custom views** receive `$paginator` and `$elements` variables from `AbstractPaginator::render()`, providing access to methods like `currentPage()`, `hasPages()`, and `nextPageUrl()`.
- **Render custom views** per-instance via `$paginator->links('vendor.pagination.custom')` or globally via `Paginator::defaultView()` in a service provider.
- **Key source files** include [`PaginationServiceProvider.php`](https://github.com/laravel/framework/blob/main/PaginationServiceProvider.php) for publishing logic and [`AbstractPaginator.php`](https://github.com/laravel/framework/blob/main/AbstractPaginator.php) for 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.