Page numbers with previous and next buttons, for content split across pages. Long ranges collapse to the first page, the last page, and the pages around the current one.
import { Pagination } from "@rdloom/react";
export default function PaginationBasicExample() {
return <Pagination pageCount={10} />;
}Installation
npx rdloom add paginationCopies the source into src/components/rdloom. Edit it freely: rdloom upgrade merges later versions into your changes. It needs react-aria-components; add --install to install them.
Use another registry client
npx shadcn@latest add https://rdloom.vimalbhatt.com/r/pagination.jsonWorks, but without upgrade tracking.
Usage
import { Pagination } from "@rdloom/react";
<Pagination pageCount={10} />Many pages
import { Pagination } from "@rdloom/react";
// Far from the ends, both sides collapse to an ellipsis.
export default function PaginationManyPagesExample() {
return <Pagination pageCount={200} defaultPage={100} siblingCount={1} />;
}Controlled
Showing results 21 to 30
import { useState } from "react";
import { Pagination } from "@rdloom/react";
// After the page changes, tell people what changed: here a polite live region.
export default function PaginationControlledExample() {
const [page, setPage] = useState(3);
const pageCount = 12;
return (
<div className="flex flex-col items-center gap-3">
<p aria-live="polite" className="text-sm">
Showing results {(page - 1) * 10 + 1} to {page * 10}
</p>
<Pagination pageCount={pageCount} page={page} onChange={setPage} size="sm" />
</div>
);
}API Reference
Defined by the spec. Components also accept the props of the React Aria component they wrap.
| Prop | Type | Default |
|---|---|---|
pageCountrequiredTotal number of pages. | number | none |
pageControlled current page, starting at 1. | number | none |
defaultPageInitial page when uncontrolled. | number | 1 |
onChangeCalled with the page the user chose. | (page: number) => void | none |
siblingCountHow many page numbers to show on each side of the current page before collapsing the rest to an ellipsis. | number | 1 |
labelAccessible name of the navigation landmark. Translate it for other languages. | string | "Pagination" |
sizeButton size. | "sm" | "md" | "md" |
isDisabledDisables every button. | boolean | false |
Accessibility
Role navigation, WCAG 2.2 AA. Tested with axe and keyboard tests; screen reader checks are in the audit checklist.
Keyboard
- Tab moves between the buttons
- Enter or Space goes to that page
Screen readers announce
- Announced as a navigation landmark named Pagination, with a list of items
- The current page is announced as the current page, e.g. "Page 3, current page"
- A disabled previous button on page 1 is announced as dimmed or unavailable
What your code must do
- A navigation landmark with an accessible name, containing a list of buttons
- The current page has aria-current="page" and is also marked by shape and weight, not just color
- Each page button is named Page N; previous and next are named Previous page and Next page and are disabled at the ends
- Ellipses are decorative (aria-hidden)
- After the page changes, move focus or announce the new content: the component only tells you the page
Guidelines
Use it when
- Lists or search results split into pages
- When people need to jump to a specific page or know how far they are
Avoid it when
- Endless feeds: use a Load more button
- A data grid: it has its own pager
Don't
- Changing the content without telling screen readers (move focus to the results heading or announce it)
- Hiding the current page number
Design tokens
The semantic tokens this component uses. Change them once and every component follows; see Design tokens.
--rd-color-action-primary--rd-color-action-on-primary--rd-color-surface-subtle--rd-color-border-default--rd-color-text-default--rd-color-text-muted--rd-color-focus-ring--rd-radius-control