Numbered pages with ellipses, previous and next, and a selected chip that glides between pages on a spring. Controlled or uncontrolled, buttons or real links.
When to use
- Moving through a long, ordered set one page at a time: invoices, search results, an audit log.
- When people need to know where they are and jump to a page, not only "load more".
- The
compactvariant ("Page 3 of 12") for table footers, cards and narrow screens.
When not to use
Installation
npx shadcn add https://eased.dev/r/pagination.jsonThe command adds Pagination, everything it builds on and Eased’s tokens to your project.
Usage
import { useState } from "react";import { Pagination } from "@/components/eased/pagination/pagination";export function InvoicesPager() { const [page, setPage] = useState(1); return <Pagination count={11} page={page} onPageChange={setPage} aria-label="Invoice pages" />;}Examples
Compact
Previous, the page number (it rolls) and next. Reach for it under about 400px, or when the count matters more than the jump.
<Pagination variant="compact" size="sm" count={11} page={page} onPageChange={setPage} />Links
getHref renders real <a> elements, so pages can be crawled and opened in a new tab. Each link still calls onPageChange.
<Pagination count={24} page={page} getHref={(p) => `/blog?page=${p}`} />More context around the current page
<Pagination count={40} defaultPage={18} siblings={2} boundaries={1} />API reference
Pagination
| Prop | Type | Default | Description |
|---|---|---|---|
count | number | How many pages there are. | |
page | number | The current page, from 1 (controlled). | |
defaultPage | number | 1 | The starting page (uncontrolled). |
onPageChange | (page: number) => void | ||
variant | "default" | "compact" | "default" | Numbered pages, or "Page 3 of 12". |
size | "sm" | "md" | "md" | 32px for tables and cards, 36px for pages. |
siblings | number | 1 | Pages either side of the current one. |
boundaries | number | 1 | Pages always shown at each end. |
getHref | (page: number) => string | Render links instead of buttons. | |
getPageLabel | (page: number) => string | `Page ${p}` | Each page's accessible name. |
disabled | boolean | false | |
aria-label | string | "Pagination" | The landmark's name. |
className | string | "" | Appended to the nav's classes. |
Other nav props pass through. paginationSlots(page, count, siblings, boundaries) is exported for building your own layout.
Keyboard
| Key | Action |
|---|---|
Tab | Moves through previous, each page and next. |
← / → | Moves focus to the previous or next control in the row. |
Enter / Space | Goes to that page. Focus follows the page if it moved to another slot. |
Accessibility
- A
navlandmark named byaria-label; the current page hasaria-current="page". - Previous and next at the ends are
aria-disabled, notdisabled, so focus is never dropped from under the keyboard. - Ellipses are hidden from screen readers; the compact variant announces "Page 3 of 12" from a polite live region that stays mounted.
- Targets are 32 or 36px and sit edge to edge with a 4px gap, so they carry no extended hit area.
Motion
The selected chip is the shared Indicator: it glides to the new page on the snappy spring and redirects mid-flight. Past seven pages the row has a fixed number of slots, so its width never changes; when the window shifts under the chip, each slot's number slides 8px in the direction of travel with a 2px blur (snappy in, 100ms out). Page numbers press by colour only, never scale, so the chip measures them true; previous and next press like icon buttons. The compact number rolls. Reduced motion: the chip jumps and numbers change in place.
Notes for AI
Use Pagination when a list is split into numbered pages the server or client already knows the count of. Wire page and onPageChange to your data fetch, or getHref for URL-driven pages; use variant="compact" in table footers and below 400px.
Don't hide previous and next at the ends or add your own selected style or animation to the page buttons: the chip, the slot swap and focus handling are built in. Hide the whole control yourself when there is only one page.