Model::paginate
Returns one page of results plus the totals needed to render numbered pagination links.
Model::paginate(Closure|int|null $perPage = null, array|string $columns = ['*'], string $pageName = 'page', int|null $page = null, Closure|int|null $total = null): Illuminate\Pagination\LengthAwarePaginatorParameters
| Parameter | Type | Description |
|---|---|---|
$$perPageoptional | Closure|int|null= null | Rows per page. When null, the model's $perPage property is used, which defaults to 15. A closure receives the total and returns the page size. |
$$columnsoptional | array|string= ['*'] | Columns to select for the page query. |
$$pageNameoptional | string= 'page' | Query string parameter the current page is read from and written to. Change it when two paginators appear on one screen. |
$$pageoptional | int|null= null | Page number to render. When null it is resolved from the current request via Paginator::resolveCurrentPage(). |
$$totaloptional | Closure|int|null= null | Total row count. Supply it to skip the extra count query, for example from a cached value. |
Return value
Illuminate\Pagination\LengthAwarePaginatorA length-aware paginator holding the current page of models plus the total count, page number and generated URLs.
Errors
Throws InvalidArgumentException when the resolved page number is not a positive integer.
Changelog
- PHP 12.1.0The declared return type of paginate() was narrowed to Illuminate\Pagination\LengthAwarePaginator.
paginate works out the LIMIT and OFFSET for you from the current page, runs a second query to count the total, and wraps both in a LengthAwarePaginator. The paginator is iterable, so a Blade @foreach over it works exactly like a collection, and {{ $users->links() }} renders the numbered links.
When to reach for it
Use it for any list a human scrolls through and where the UI shows page numbers or a total. Returning a paginator straight from a controller produces a JSON envelope with data, total, current_page and the page URLs, which is why it is the default shape for index endpoints.
What trips people up
The page number comes from the request, not from your code — ?page=3 is read automatically. Outside an HTTP request, in a job or a command, no page is resolved and you always get page one unless you pass the fourth argument explicitly.
Two paginators on a single screen both read page and therefore move together. Pass a distinct $pageName to each.
The count query is not free. If you only need next and previous links, simplePaginate skips it; for large or fast-moving tables cursorPaginate avoids OFFSET scans entirely.
Example
<?php
namespace App\Http\Controllers;
use App\Models\User;
use Illuminate\View\View;
class UserController extends Controller
{
public function index(): View
{
$users = User::where('votes', '>', 100)->paginate(15);
echo $users->total(); // 50
echo $users->perPage(); // 15
echo $users->currentPage(); // 1 (or whatever ?page= says)
echo $users->lastPage(); // 4
echo $users->count(); // 15 — items on this page
echo $users->nextPageUrl(); // http://laravel.app?page=2
return view('user.index', ['users' => $users]);
}
}When to use it
- Render a paginated table of users with numbered page links in Blade.
- Return a JSON index endpoint that includes total and current_page metadata.
- Show two independently paged lists on the same admin screen.
- Skip the count query on a huge table by supplying a cached total.
Frequently asked questions
How does Laravel know which page to show?
paginate() reads the page query string parameter from the current request through Paginator::resolveCurrentPage(). You can override it by passing the page number as the fourth argument, which is required outside an HTTP request.
Why do both paginators change together on one page?
They are both reading and writing the same page parameter. Pass a distinct third argument, the $pageName, to each call so one uses ?users=2 and the other ?posts=2.
How do I avoid the extra count query from paginate()?
Use simplePaginate() when next and previous links are enough, or pass a known total as the fifth argument. For large tables, cursorPaginate() avoids both the count and the growing OFFSET scan.
More examples
Two paginators on one screen
The third argument controls both the parameter the paginator reads and the one it writes into its links, so clicking through one list no longer resets the other. withQueryString() carries the rest of the current query string, which is what keeps sort and filter selections alive across page clicks.
<?php
use App\Models\Post;
use App\Models\User;
// Both read ?page= and would move together.
$users = User::paginate(15);
$posts = Post::paginate(15);
// Give each its own query string parameter.
$users = User::paginate($perPage = 15, $columns = ['*'], $pageName = 'users');
$posts = Post::paginate($perPage = 10, $columns = ['*'], $pageName = 'posts');
echo $users->getPageName(); // users
echo $posts->getPageName(); // posts
echo $users->nextPageUrl(); // http://laravel.app?users=2
// Keep existing filters on the links:
$users = User::paginate(15)->withQueryString();Paginating outside an HTTP request
There is no request in a console command, so Paginator::resolveCurrentPage() falls back to 1 every time. Passing $page explicitly is what makes the loop advance — though for a pure export, chunkById is both cheaper and safer since it avoids the repeated count query and the growing OFFSET.
<?php
namespace App\Console\Commands;
use App\Models\User;
use Illuminate\Console\Command;
class ExportUsers extends Command
{
public function handle(): void
{
$page = 1;
do {
// Without the 4th argument this would loop on page 1 forever.
$users = User::orderBy('id')->paginate(100, ['*'], 'page', $page);
foreach ($users as $user) {
$this->line($user->email);
}
$page++;
} while ($users->hasMorePages());
}
}Choosing between the three paginators
paginate needs total() and lastPage(), and pays for them with an extra count query on every request. simplePaginate drops both and issues one query. cursorPaginate replaces OFFSET with a where clause on the ordered column, so page 500 costs the same as page 1 — but it cannot produce numbered links.
<?php
use App\Models\User;
// Numbered links. Runs a count query. LengthAwarePaginator.
$a = User::paginate(15);
echo $a->total(); // 5000
echo $a->lastPage(); // 334
// Next/Previous only. No count query. Paginator.
$b = User::simplePaginate(15);
echo $b->hasMorePages() ? 'more' : 'end'; // more
// Keyset pagination. Requires an order by. CursorPaginator.
$c = User::orderBy('id')->cursorPaginate(15);
echo $c->nextPageUrl(); // http://localhost/users?cursor=eyJpZCI6MTUs...
// Skip the count when you already know the total.
$d = User::paginate(perPage: 15, total: 5000);
echo $d->lastPage(); // 334
Discussion