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 pageTotal pagesSiblingsRendered buttons
11211 2 3 … 12
51211 … 4 5 6 … 12
111211 … 10 11 12
51221 … 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

PropTypeDefaultDescription
page*numberThe currently active page (1-based).
totalPages*numberTotal number of pages.
onPageChange*(page: number) => voidCalled with the new page number when the user navigates.
siblingsnumber1Number of page buttons to show on each side of the current page. Increasing this reduces ellipsis usage.
classNamestringAdditional classes on the nav element.

ARIA roles

ElementRoleKey attributes
Containernavigationaria-label='Pagination'
Current pagebutton (implicit)aria-current='page', aria-label='Page N'
Page buttonbutton (implicit)aria-label='Go to page N'
Prev / Nextbutton (implicit)aria-label='Go to previous/next page', disabled at boundary
Ellipsis(decorative)aria-hidden='true'

Keyboard

KeyAction
TabMove focus forward between Prev, page buttons, and Next
Shift+TabMove focus backward through the pagination buttons
Enter / SpaceNavigate 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.