Pagination
Page navigation with an ellipsis algorithm that keeps the control compact regardless of page count. Always controlled — the consumer manages the current page.
Preview
Page 5 of 12
Ellipsis algorithm
The pagination always shows the first and last pages. Middle pages are shown based on the siblings prop. Ellipses appear when there are more than two pages between the visible range and the edges.
| Current page | Total pages | Siblings | Rendered buttons |
|---|---|---|---|
| 1 | 12 | 1 | 1 2 3 … 12 |
| 5 | 12 | 1 | 1 … 4 5 6 … 12 |
| 11 | 12 | 1 | 1 … 10 11 12 |
| 5 | 12 | 2 | 1 … 3 4 5 6 7 … 12 |
Implementation
Pagination.tsx
tsx
import { Pagination } from "@/components/ui/Pagination";
import { useState } from "react";
function ResultsList() {
const [page, setPage] = useState(1);
const TOTAL_PAGES = 12;
return (
<div>
{/* ...results for page {page}... */}
<Pagination
page={page}
totalPages={TOTAL_PAGES}
onPageChange={setPage}
/>
</div>
);
}
// More siblings — fewer ellipses on wide screens
<Pagination page={5} totalPages={20} onPageChange={setPage} siblings={2} />Props
| Prop | Type | Default | Description |
|---|---|---|---|
page* | number | — | The currently active page (1-based). |
totalPages* | number | — | Total number of pages. |
onPageChange* | (page: number) => void | — | Called with the new page number when the user navigates. |
siblings | number | 1 | Number of page buttons to show on each side of the current page. Increasing this reduces ellipsis usage. |
className | string | — | Additional classes on the nav element. |
ARIA roles
| Element | Role | Key attributes |
|---|---|---|
| Container | navigation | aria-label='Pagination' |
| Current page | button (implicit) | aria-current='page', aria-label='Page N' |
| Page button | button (implicit) | aria-label='Go to page N' |
| Prev / Next | button (implicit) | aria-label='Go to previous/next page', disabled at boundary |
| Ellipsis | (decorative) | aria-hidden='true' |
Keyboard
| Key | Action |
|---|---|
| Tab | Move focus forward between Prev, page buttons, and Next |
| Shift+Tab | Move focus backward through the pagination buttons |
| Enter / Space | Navigate to the focused page or Prev/Next button's target |
Accessibility
- →The container uses <nav aria-label="Pagination"> — it appears as a landmark for quick keyboard navigation.
- →Each page button has an aria-label describing its action ("Go to page 3") rather than just the number.
- →The active page uses aria-current="page" — screen readers announce "current" for the selected page.
- →Previous and next buttons use descriptive aria-label values and are disabled (with disabled attribute) when at the boundary.
- →Ellipsis spans are aria-hidden — they are decorative and screen readers skip them.