November 25, 2025

Laravel Offset Pagination vs Cursor Pagination: When to Use What (With Real-World Examples)

Tutorial
Laravel Offset Pagination vs Cursor Pagination: When to Use What (With Real-World Examples)

Laravel gives us two main strategies for paginating large datasets:

  1. Offset Pagination (paginate())
  2. Cursor Pagination (cursorPaginate())

Both work beautifully — but they solve different problems. Picking the wrong one can lead to slow queries, inconsistent results, and a page 2 that sometimes returns items from page 1 (yikes).

Let’s break this down the practical way.


1. Offset Pagination (paginate)

This is the classic pagination style:

$users = User::paginate(20);

It runs a query like:

SELECT * FROM users ORDER BY id LIMIT 20 OFFSET 40;

When Offset Pagination Is a Good Fit

  • Small or medium datasets
  • Admins dashboards where precision > performance
  • Pagination where users jump to a specific page (page 10, page 50…)
  • Filtering + sorting + searching

When Offset Falls Apart

Offset pagination is slow when:

  • The table has millions of rows
  • There is no good index for the sort column
  • The user scrolls deep into the list (OFFSET 400k = painful)

Also: if new data is inserted while the user paginates, pages may shift.

Page 2 might show items from page 1 again because rows moved.

Real-World Example

Admin panel showing customers

You want to allow:

  • Sorting by name
  • Jumping to page numbers
  • Filtering by status
  • Going back and forth predictably

Offset pagination handles all of this.

Example:

$customers = Customer::orderBy('created_at', 'desc')->paginate(50);

Users want precision and navigation, not infinite-scroll speed, so offset works perfectly.


2. Cursor Pagination (cursorPaginate)

Cursor pagination doesn’t use OFFSET. Instead, it remembers the last item’s position and fetches from there:

$posts = Post::orderBy('id')->cursorPaginate(20);

This generates queries like:

SELECT * FROM posts WHERE id > last_seen_id ORDER BY id LIMIT 20;

When Cursor Pagination Shines

  • Huge datasets (millions+ rows)
  • Infinite scrolling (social feeds)
  • Real-time data (chat messages, logs)
  • When new data is constantly inserted
  • When avoiding duplicates is important

Limitations

Cursor pagination does not support:

  • Jumping to page numbers
  • Sorting by arbitrary columns (must be unique and indexed)
  • Some complex filtering scenarios

Real-World Example

Activity Feed / Timeline (e.g., Facebook-style feed)

You want:

  • Infinite scroll
  • New posts arriving constantly
  • No repeated or missing posts

Cursor pagination guarantees consistency.

Example:

$activities = Activity::orderBy('id', 'desc')->cursorPaginate(20);

Even if 100 new activities are added while the user scrolls, the feed does not reshuffle or repeat.


3. Which One to Use? (Simple Rule)

Use Offset Pagination when:

  • You need traditional pagination with page numbers
  • You need flexible sorting or filtering
  • Dataset is manageable (< few hundred thousand rows)
  • Admin dashboards, reports, tables

Use Cursor Pagination when:

  • You’re dealing with huge datasets
  • You want infinite scrolling
  • Data changes frequently
  • You can sort by a unique, indexed column (usually id or timestamps)

Quick Code Comparison

Offset Pagination

$logs = Log::orderBy('created_at', 'desc')->paginate(50);

Cursor Pagination

$logs = Log::orderBy('created_at', 'desc')->cursorPaginate(50);

Notice the key difference:

Cursor pagination requires the sorted column to be:

  • Indexed
  • Unique
  • Stable

Offset pagination doesn’t.


Conclusion

Both methods are great — but they're built for different jobs.

  • If you're building something UI-heavy like a dashboard table → Offset Pagination
  • If you're building something real-time or massive → Cursor Pagination

Use the tool that fits the problem, not the other way around.

Did you find this article helpful? Share it!