Back to all posts
Laravel Development

Laravel paginate() vs cursorPaginate(): Which One Should You Use?

Admin
December 17, 2025
Laravel paginate() vs cursorPaginate(): Which One Should You Use?

When a table grows from a few hundred rows to hundreds of thousands, pagination stops being “just a helper” and becomes a performance decision. In Laravel, the two main options you will reach for are paginate() (offset-based) and cursorPaginate() (cursor-based).​

This guide explains both approaches, how they work under the hood, and when to choose one over the other in real projects.​

Quick recap: what is pagination?

Pagination splits a large result set into smaller pages so the user never has to load everything at once. Laravel ships with multiple pagination helpers built into Eloquent and the query builder.​

Common methods are:

  • paginate() – classic, offset-based pagination with full meta (total, last page, etc.).​
  • simplePaginate() – similar to paginate() but lighter, without total count.​
  • cursorPaginate() – cursor-based pagination optimized for large or frequently changing datasets.​

paginate(): classic offset pagination

paginate() is the familiar “page 1, 2, 3…” style pagination most apps start with.​

How paginate() works

Under the hood, Laravel generates SQL with LIMIT and OFFSET, plus an extra query to count total rows.​

$users = User::paginate(10); // 10 records per page

This gives you:

  • A collection of models for the current page.​
  • Metadata like total, per_page, current_page, last_page.​
  • Ready-made Blade links:

{{ $users->links() }}

When paginate() is a good fit

Use paginate() when:​

  • The dataset is small to medium sized (e.g., admin tables, reports).
  • Users need to jump directly to a page (1, 5, 10, last).
  • You care about seeing “Showing 1–10 of 243 results” style information.

For dashboards, CMS-style back offices, and rarely changing tables, paginate() is usually enough.​

cursorPaginate(): pagination for big and “live” data

cursorPaginate() was added to solve performance and consistency issues that appear with offset pagination on large or highly dynamic tables.​

How cursorPaginate() works

Instead of skipping N rows with an offset, cursor pagination remembers the last item and continues from there.​

$users = User::orderBy('id')->cursorPaginate(10);

The generated SQL looks like:

SELECT * FROM users WHERE id > last_seen_id ORDER BY id LIMIT 10;

The “cursor” travels in the URL as an encoded string (e.g., ?cursor=eyJpZCI6MTUsIl9wb2ludHNUb05leHRJdGVtcyI6dHJ1ZX0). Laravel decodes that and knows where to resume.​

In Blade you might do:

@foreach ($users as $user) {{ $user->name }} @endforeach

@if ($users->previousPageUrl()) Previous @endif

@if ($users->nextPageUrl()) Next @endif

When cursorPaginate() shines

Cursor pagination is ideal when:​

  • The table is huge (hundreds of thousands or millions of rows).
  • You implement infinite scroll or “Load more” APIs.
  • New rows are inserted frequently, and you want to avoid users seeing duplicates or missing records when they move to the next page.

This approach avoids scanning and skipping a large number of rows and does not need a full count query, which makes it far more scalable.​

Key differences at a glance

Behaviour comparison

Aspect

paginate() (offset)

cursorPaginate() (cursor)

Pagination style

Page numbers (?page=3)​

Cursor token (?cursor=.exndjd..)​

SQL pattern

LIMIT + OFFSET​

WHERE column > last_value LIMIT N​

Total rows / last page

Available (total, last_page)​

Not available by default; no total count​

Performance on big data

Degrades as page number grows (large offsets) ​

Stable even on very large tables​

Consistency on live data

Can repeat or skip rows if new data is inserted​

More consistent because it anchors to a specific record position​

Direction

Can go forward and backward with page numbers​

Primarily forward; backward support is more limited and implementation-specific​

Typical use cases

Admin panels, reports, small/medium datasets​

APIs, infinite scroll, timelines, activity feeds, massive tables ​

Choosing the right method

A simple rule of thumb for your own article or codebase:​

  • Prefer paginate() when:
    • The table is not massive.
    • You want total counts and numbered navigation.
  • Prefer cursorPaginate() when:
    • You work with large or frequently updated data.
    • You build APIs, mobile feeds, or infinite scroll where performance is critical.

You can also mix both in the same project: paginate() for admin UIs and cursorPaginate() for public, high-traffic endpoints.​