Browse the docs

Pagination

Page navigation, with the middle collapsed to an ellipsis once the list gets long.

page 4 of 12

Props

PropValuesDefaultDescription
pagenumberCurrent page, 1-based.
totalnumberTotal number of pages. Renders nothing when it is 1 or less.
siblingsnumber1Pages shown either side of the current one.
hrefFor(page) => stringRenders links.
onPageChange(page) => voidRenders buttons.
labelstring"Pagination"Accessible name for the <nav>.
classNamestring""Appended after the component's own classes.

Common patterns

// Links — the better default
<Pagination page={page} total={12} hrefFor={(n) => `/posts?page=${n}`} />

// Buttons, when the list is filtered client-side
const [page, setPage] = useState(1)

<Pagination page={page} total={pageCount} onPageChange={setPage} />

// Wider window on a desktop-only table
<Pagination page={page} total={40} siblings={2} hrefFor={hrefFor} />

Notes

Prefer hrefFor. Links are shareable, they survive a page reload, they work before hydration, and the browser gives you open-in-new-tab for free. Reach for onPageChange only when the pages exist solely in memory.

Pass both and hrefFor wins — the component renders links and ignores the callback.

The current page carries aria-current="page", and the arrows have visually hidden labels, so the control announces sensibly without a title attribute.

The ellipsis is aria-hidden, because "…" read aloud between two numbers is noise. If you need the jump-to-page affordance that some designs put there, add a Select beside the nav instead.