🚀 LEVEL UP TO SENIOR:Unlock 500+ Advanced Practical Challenges & Exercises.
🎓 COURSERA PARTNER:Earn professional Google, Meta, and IBM certificates to supercharge your resume.
HTML MASTER CLASS /// LEARN TAGS /// BUILD STRUCTURE /// SEMANTIC WEB /// HTML MASTER CLASS /// LEARN TAGS ///

Cursor Pagination

Implementing cursor-based (keyset) pagination for consistent, high-performance pagination at any scale.

Total XP: 0|💻 backend XP: 0

Skill Matrix

UNLOCK NODES BY LEARNING NEW TAGS.

Select an unlocked node to view details root

🚀 LEVEL UP TO SENIOR:Unlock 500+ Advanced Practical Challenges & Exercises.
🎓 COURSERA PARTNER:Earn professional Google, Meta, and IBM certificates to supercharge your resume.

1Step-by-Step Breakdown

The Core Idea: Position by Value, Not by Count. Rather than saying "skip the first N rows" (offset pagination's approach), cursor pagination says "give me rows that come after THIS specific value" — the cursor encodes the last-seen row's sort key, and the next page's query filters directly on that value instead of counting through preceding rows.

Why This Solves the Performance Problem. A cursor-based query filters directly on an indexed column's value, which the database can seek to efficiently regardless of how deep into the dataset that value falls — unlike offset pagination, query performance for a cursor-based "page 5000" is essentially identical to "page 1," since there's no preceding-row scan required.

Why This Solves the Consistency Problem Too. Because a cursor references a specific value rather than a position, a row inserted or deleted elsewhere in the dataset between two page fetches doesn't shift anything — the cursor still correctly identifies "everything after this exact value," making cursor pagination immune to the skip/duplicate issue offset pagination has under concurrent writes.

Encoding the Cursor: Opaque to the Client. A well-designed cursor is an opaque, base64-encoded token the client passes back verbatim, without needing to understand or construct its internal structure — this lets the server change the cursor's internal encoding later without breaking any client that treats it as an opaque string.

A Complete Cursor Pagination Implementation. The response includes both the current page of results and an encoded nextCursor (derived from the last row returned) — the client simply passes that nextCursor value back as the cursor query parameter to fetch the subsequent page, with no page-number concept involved at all.

The Tradeoff: No Arbitrary Page Jumping. Cursor pagination's core limitation is genuine: a client cannot jump directly to "page 15" the way offset pagination allows, since each cursor only knows how to move to the immediately next (or previous) page relative to itself — a UI needing arbitrary page-number navigation (a numbered page list) is a poor fit for pure cursor pagination.

When Cursor Pagination Is the Right Choice. Cursor pagination is the clear right choice for infinite-scroll UIs (social feeds, activity logs), very large or rapidly-changing datasets where offset's consistency problems matter, and any high-traffic endpoint where deep-page performance genuinely needs to stay flat regardless of dataset size.

What is the primary reason cursor-based pagination maintains roughly constant query performance regardless of how deep into a dataset a client is paging, unlike offset pagination?

  • The database seeks directly to the cursor's indexed value, without needing to scan and discard preceding rows
  • Cursor pagination automatically caches every previous page's results

Level Up 🚀

Advanced cheat sheets, SEO tricks, and interview prep for this topic.

Browser Support

ChromeSupported

Fully supported.

FirefoxSupported

Fully supported.

SafariSupported

Fully supported.

EdgeSupported

Fully supported.

Accessibility (A11y)

1Consistent, Flat Performance From Cursor Pagination Benefits Users on Slower Connections Scrolling Deep Into Content

A user with a slower connection or older device scrolling deep into an infinite-scroll feed benefits directly from cursor pagination's consistent performance at any depth, avoiding the progressively slower load times that offset pagination would otherwise introduce the further they scroll.

SEO Implications

  • 1

    Cursor Pagination Keeps Response Times Flat for High-Traffic, Deeply-Paged Endpoints

    For a high-traffic endpoint serving a large, actively-growing dataset, cursor pagination's constant-time performance characteristic (regardless of page depth) directly protects server response time and page-speed-related search ranking signals, compared to offset pagination's degrading performance at scale.

Best Practices

Encode cursors as opaque tokens the client passes back verbatim, never exposing their internal structure directly

This decouples clients from the cursor's internal implementation, letting the server change that structure later without breaking any existing client integration.

Choose cursor pagination specifically for infinite-scroll UIs, very large or rapidly-changing datasets, and high-traffic endpoints needing flat performance at scale

This matches the technique to the use cases where its genuine benefits (constant performance, consistency under concurrent writes) clearly outweigh its core limitation of not supporting arbitrary page-number jumping.

Frequent Bugs

THE BUG

An infinite-scroll feature's load time noticeably degrades the deeper a user scrolls, or occasionally shows a duplicated item as more content loads.

THE FIX

This is the classic signature of offset-based pagination being used for a use case (infinite scroll on a large or actively-changing dataset) that cursor-based pagination is specifically designed to handle well. Migrate the endpoint to cursor pagination for consistent performance and immunity to the concurrent-write consistency issue.

Real-World Examples

Migrating an Activity Feed From Offset to Cursor Pagination

A social activity feed using offset pagination showed increasingly slow load times as users scrolled deep into their history, with response times climbing from under 50ms on the first page to over 2 seconds by the 50th page of infinite scroll. Migrating to cursor-based pagination, using a composite (createdAt, id) cursor matching the feed's existing sort order and a supporting database index, brought response times down to a consistent, flat 40-60ms regardless of how deep into the feed a user had scrolled — since the database could now seek directly to the requested cursor position instead of scanning through and discarding all preceding rows.

// After migration: consistent response time at any scroll depth
WHERE (created_at, id) < (?, ?) ORDER BY created_at DESC, id DESC LIMIT 20

Interview Prep

Pascual Vila

Pascual Vila

Full-Stack Software and AI Engineer

Full-Stack Software and AI Engineer with 6 years of experience building enterprise-grade web applications across React, Angular, Node.js, and Python. Recently completed a Master's in AI Development specializing in LLMs, RAG, and AI agent architectures, and currently builds enterprise systems that integrate AI and Digital Twins to optimize industrial and logistics processes.

LinkedIn ↗
Common Pitfalls & Errors

The Error //

Exposing the cursor's internal structure directly to the client instead of an opaque encoded token

// Wrong: internal structure exposed directly ?cursorCreatedAt=2024-01-15&cursorId=42 // Correct: opaque, encoded token ?cursor=eyJjcmVhdGVkQXQiOiIyMDI0LTAxLTE1IiwiaWQiOjQyfQ==

The Solution //

If a client can see and potentially construct or manipulate the cursor's internal fields directly, it couples the client to the server's internal implementation, making it much harder to change the cursor's structure later without breaking existing clients — and it also risks a client crafting an invalid or malicious cursor value.

The Error //

Choosing cursor pagination for a UI that genuinely requires arbitrary page-number jumping

// Cursor pagination cannot support this UI requirement: // "Page: [1] [2] [3] ... [47] [Next]" — jump to page 23 directly // For this need, offset pagination is the better-suited choice

The Solution //

Cursor pagination fundamentally only supports sequential movement (the next or previous page relative to a specific cursor) — it cannot support jumping directly to an arbitrary page number, since there's no concept of page numbers at all. For a UI genuinely requiring this (like an admin table with numbered page links), offset pagination remains the more appropriate choice despite its other tradeoffs.

Continue Learning