Pagination
Concept
A screen element that cuts a long list into fixed-size pages and lets people move between them with page numbers or previous and next controls.
Why it exists
Back when a list held twenty items, there was no reason to split it. Draw all of them on one screen, let the user scan down the column, done. The trouble starts the moment that list holds 3,000 items.
Volume jams three places at once. The database spends time pulling 3,000 rows, the network spends time shipping them, and the browser spends time drawing 3,000 lines. The list screen takes eight seconds to appear, and in those eight seconds the user clicks the same link again.
What makes it worse is that the user needed five of those 3,000 rows. The document they were after sits near the top, and the 2,900 rows below it get prepared, sent, rendered, and thrown away.
So the list gets cut. Fetch only as much as fits on screen, and fetch the next batch when the user signals they want more. Pagination is the version of that cut that stays visible: the screen says which page you are on and how many pages exist in total. That visibility is the whole character of the pattern, and it is also what people give up when they choose the alternatives.
How it works
Divide the list by how many rows belong on one page, and have the screen tell the server which slice it needs. With 20 rows per page, clicking page 3 sends a request for rows 41 through 60.
There are two ways to say where to start.
- Offset — send a number for how many rows to skip. Page 3 means skip 40, take 20. Page numbers map directly onto that arithmetic, so a button that jumps straight to page 7 is easy to build, and counting the full result set gives you a total page count to display.
- Cursor — hold a marker for the last row you received and ask for whatever comes after it. Nothing gets skipped, so speed stays flat no matter how long the list grows, and boundaries hold steady even while the list changes underneath. In exchange you only ever know the page immediately before and after, which means no page numbers.
Offset has a slowdown built into it. Click page 5,000 and the database counts through 99,980 rows before handing back twenty. It also drifts when the list changes: read page 1, let someone insert a new row at the top, and everything shifts down one slot, so page 2 opens with the row that ended page 1.
The control itself is usually built from four parts.
- Page numbers — mark the current page clearly and show two or three on either side. With 300 pages you can't lay them all out, so keep the first and last at the ends and collapse the middle into an ellipsis.
- Previous and next — move one step at a time. On page 1, leave the previous button in a disabled state rather than removing it; taking the slot away shoves every other button sideways.
- Scale — say which rows out of how many are on screen. Four search results and the user reads them all; four thousand and they go back to fix the query.
- Rows per page — offer 20, 50, 100. Someone scanning for a pattern picks 100; someone on a slow connection picks 20.
Accessibility is the part that most often goes missing here. Accessibility means designing so the product works regardless of disability or circumstance. A bare row of numbers reaches a screen reader as "1, 2, 3" with no context, so each link needs text saying which page it leads to, and the current page needs to be marked as the current location rather than only styled differently.
Where it shows up
Use it on screens where people hunt down a specific row and act on it. Admin screens carry most of the examples — user lists, support tickets, billing records — and the control usually sits directly under a table, the element that lays data out in rows and columns. On screens like these, going back to a row you saw a minute ago is routine, and "page 3" is what remembers where that row was.
It also sits under lists of cards arranged in a grid. There the page change should scroll the view back to the top of the list; if the next page loads while the user is scrolled halfway down, they start reading the new page from its middle.
The places to avoid it are just as clear. For a timeline or a notification inbox — anything read top to bottom in one pass — infinite scroll fits better, because finishing the list isn't the goal and returning to a specific item rarely comes up. Tabs play a different role entirely: tabs move between different kinds of content, while pagination moves through one kind in order.
How it sounds at work
"Should we drop pagination on this list and go infinite scroll? Or a load-more button?"
A proposal to remove the page numbers and append batches instead.
"We don't need the total count. Let's go cursor-based and just keep a next button."
Give up showing how many pages there are in exchange for speed.
"Open page 3 and the last row of page 2 shows up again. Feels like the sort order is shifting."
A symptom of offset paging where the list changed between requests.
This came up loudly once on Pagedeck's admin screen. Document lists for enterprise accounts passed a hundred thousand rows, later pages started taking several seconds, and Theo traced it to offset paging. Switching to cursors meant giving up both page numbers and the total count.
Noa checked what people were actually doing on that screen first. Support staff weren't jumping around by page number; they were searching for the document by name, and requests for later pages amounted to a handful a day. So the page numbers came out and previous, next, and search stayed. Rows per page stayed too.
What people get wrong
The common reading is that pagination is dated and infinite scroll is what replaced it. Newer products rarely show page numbers, so it looks that way. The two aren't old and new, they serve different jobs: scrolling suits a list you skim, pages suit a list you work through. Rebuild an admin screen on infinite scroll and support staff will scroll from the top every time they want a row they saw five minutes ago.
Another frequent mistake is assuming that removing page numbers reduces database load. Changing the control on screen and changing the request underneath are separate jobs. Swap the buttons for previous and next while still sending an offset, and every reason the screen was slow is still there. The cost lives in counting through tens of thousands of rows, not in the numbers drawn at the bottom of the page.
Easily confused with
| Term | How it fetches the next batch | How it differs |
|---|---|---|
| Infinite scroll | Appends more rows on its own when you reach the end | The user never picks a page and no position is left behind. Good for lists you skim, wrong for lists you return to |
Where to go next
- UI component
see which family of parts this element belongs to
- Tabs
compare it against the other way of swapping content in place
Last updated 2026-09-16