Offset pagination drifts when the data changes — insert a row at the top and every page shifts down, so the same item can surface twice (or get skipped entirely).
Cursor pagination stays stable: it anchors to a specific row instead of a numeric position, so inserts and deletes never create duplicates.
Three patterns fit different jobs — numbered for jumping to any page, load-more for on-demand appends, infinite scroll for continuous feeds.
Never render every page link. Truncate to first, last, current, and its immediate neighbors, using an ellipsis for the gaps.
Keep the page number in the URL (?page=500) so a refresh stays put and the view becomes a shareable link.
When users return from a detail view, restore their scroll position instead of dumping them back at the top of the list.
Do / Don't
Do: Reach for cursor pagination when rows are inserted or deleted often, to avoid duplicates and skips
Do: Persist the current page in the URL so refreshes and shared links land on the same page
Do: Collapse long page ranges to first, last, current, and neighbors with an ellipsis
Don't: Render hundreds of numbered links — they spill off-screen and overwhelm
Don't: Reset an infinite-scroll list to the top when the user comes back from a detail view
The Design System Blueprint
An 11-page visual guide to consistent, scalable UI: colors, typography, spacing, components, motion.