Previous / Next Pagination
Two step links either side of a live "Page 3 of 10" readout.
6 frameworksBeginner
A numbered page strip that collapses long runs to an ellipsis.
<!--
Page 1 of 10, so Previous has nowhere to go. An anchor with no href is not
focusable and exposes no link role - which is the closest an <a> gets to a
disabled <button> - and aria-disabled tells a screen reader why it is inert
rather than leaving it to be silently skipped.
The ellipsis is aria-hidden: the numbers on either side of it already say
pages were skipped, and "horizontal ellipsis" read aloud between 4 and 10 is
noise, not information.
-->
<nav class="pager" aria-label="Pagination">
<ul class="pager__list">
<li><a class="pager__link pager__link--disabled" aria-disabled="true">Previous</a></li>
<li><a class="pager__link pager__link--current" href="?page=1" aria-current="page">1</a></li>
<li><a class="pager__link" href="?page=2">2</a></li>
<li><a class="pager__link" href="?page=3">3</a></li>
<li><span class="pager__ellipsis" aria-hidden="true">…</span></li>
<li><a class="pager__link" href="?page=10">10</a></li>
<li><a class="pager__link" href="?page=2">Next</a></li>
</ul>
</nav>| Prop | Type | Default | Description |
|---|---|---|---|
currentPagerequired | number | - | The active page number, starting at 1. |
totalPagesrequired | number | - | How many pages exist in total. |
buildHrefrequired | (page: number) => string | - | Builds the URL for a page number, so pages stay real links. |
siblingCount | number | 1 | How many page numbers to show either side of the current one. |
ariaLabel | string | 'Pagination' | The button's accessible name. Required for icon-only buttons. |
className | string | - | Additional classes merged onto the root element. |
The piece worth keeping is that every page is an `<a href>`. Pagination is navigation, and an anchor is what makes page 7 bookmarkable, middle-clickable into a new tab, crawlable, and reachable with JavaScript off - a `<button>` calling `router.push` looks identical and quietly loses all four. Because the page number lives in the URL rather than in state, the Next.js tab is a Server Component and ships no JavaScript at all; swap `<a>` for `<Link>` if you want client-side transitions. `pageRange` returns a union of numbers and gap markers rather than sentinel values like `-1`, which is what stops a gap ever being rendered as a link to page NaN. Tune `siblingCount` for how many pages flank the current one; the ellipsis stays `aria-hidden` because the numbers either side of it already say pages were skipped.