Pagination
Moves a reader through a set too long for one page, either page by page or by number. Use the table bar under a Data table, and the page numbers under a list of results.
Live preview
Rendered by the component itself from the same generated tokens as the rest of the site.
const [page, setPage] = useState(1)
<PaginationNav
page={page}
totalPages={30}
onChange={setPage}
/>Installation
Install it with the shadcn CLI, along with everything it uses. New project? Set it up first, as Quick start shows.
npx shadcn@latest add https://www.graphite-ui.com/r/pagination.jsonimport { Pagination, PaginationNav } from '@/components/ui/pagination'Anatomy
The table bar: items per page, the range shown, a page picker with its total, then previous and next, each section divided by a subtle rule. The page-number form is the kit’s second set: the arrows around the page items, the current one marked.
- Previous and nextRequiredGhost icon-only Buttons at the bar's size, named "Previous page" and "Next page", disabled at the ends.
- Items per pageOptionalThe table bar's Advanced type. The governed Select, inline, labelled "Items per page:".
- RangeOptionalThe table bar's Advanced type. "1–100 of 100 items" in on-surface-variant, filling the middle.
- PageRequiredAdvanced draws a page picker (the governed Select with its label hidden) and its total, "of 10 pages"; Simple and Unbound draw "Page 1".
Variants
Size sets the row: 48, 40 or 32. The table bar has three types; Simple and Unbound look the same, and differ in whether the total is known.
API reference
Generated from the contract, versioned with it, and checked by drift-check. This table cannot describe props the component does not have.
| Prop | Type | Default | Notes |
|---|---|---|---|
| type | 'advanced' | 'simple' | 'unbound' | — | The kit's Pagination - Table bar Type. Unbound is for a total that is not known, so Next follows hasNext. The kit draws Simple and Unbound identically. |
| size | 'sm' | 'md' | 'lg' | — | The kit's Size, a 32, 40 or 48 row. Large by default. |
| page / pageSize / pageSizes / totalItems / onChange | — | — | Controlled. onChange receives the next page and page size; changing the page size returns to page 1. |
| hasNext | boolean | — | Unbound only. |
| page-number form | — | — | The kit's Pagination - Nav, a separate export. Square page items at the same three sizes between previous and next, the current page marked; at most itemsShown entries (seven, as the kit draws), the first and last pages always shown, and an ellipsis for each run left out, which opens those pages as a native select (the kit's Overflow item). Wrapped in a nav landmark. |
Design tokens
Every swatch is live. Change the source color in the header and this table repaints, because it reads the same roles the component does.
elevationThe table bar, elevation-01 (the kit's layer-01); page-item and overflow hover, elevation-02 (the kit's background-hover).outlineThe table bar's top rule and its section dividers, through the subtle step (outline-subtle, the kit's border-subtle-00).on-surfacePage numbers, the page label and total.on-surface-variantThe range text.primaryThe current page's 16px rule (the kit's border-interactive), and the focus ring, through the family's focus step.textBody/3, and Title/5 SemiBold for the current page.spacingItem sizes, the bar's height and the section padding.
Usage
The contract's prohibitions, written as the choices you will actually face.
Do
- Use the table bar under a Data table, in its footer slot, so the table and its pages read as one.
- Use Unbound when the total is not known, and tell it whether there is a next page with hasNext.
- Return to page 1 when the page size changes; the component does this for you.
- Keep the page numbers short. Seven entries, ellipses included, is the kit’s window.
Don’t
- Restyle the arrows or the pickers. They are the governed Button and Select.
- Mark the current page by colour alone. It takes the weight and the rule, and aria-current.
- Use pagination for a list short enough to show at once.
- Put the page numbers in a table footer. That is the table bar’s place.
Accessibility
What the component does for you, and what it leaves to you.
- Roles
- The page numbers sit in a
navlandmark, each item a button named “Page n”, the current one witharia-current="page". The table bar is a labelled group. - Keyboard
- Tab reaches the arrows, the items and the pickers in order. The ellipsis is a native select, so the arrow keys move through the pages it hides.
- Names
- The arrows are “Previous page” and “Next page”; the page picker keeps “Page” as its name with the label hidden; the ellipsis names the range it hides.
- Focus
- The items take a 2px
--graphite-primary-focusring inside their square; the arrows and pickers bring their own.
Figma parity
The kit’s Pagination page has two public sets: Pagination - Nav and Pagination - Table bar. The code exports one component for each.
| Kit axis | Values | Code | How it maps |
|---|---|---|---|
| Set | Pagination - Nav | PaginationNav | Square items between the arrows; the current page SemiBold with a 16px primary rule. |
| Set | Pagination - Table bar | Pagination | On elevation-01 with an outline-subtle top rule and dividers. |
| Size | Large · Medium · Small | size | 48, 40 and 32. The kit draws the bar 50, 42 and 34, two over its controls, the same +2 Button carries; built at the controls’ height plus the rule. |
| Type | Advanced · Simple · Unbound | type | Advanced: items per page, the range, the page picker and its total. Simple and Unbound: “Page n”. The kit draws the last two identically. |
| Nav: Overflow | Boolean | itemsShown | The ellipsis opens the hidden pages as a native select, the kit’s Overflow item and its menu. |
| Item state | Enabled · Hover · Focus · Selected | — | Hover (elevation-02) and Focus are pseudo-classes (governance rule 7); Selected is the current page. |
