Pagination
Page-by-page navigation across a long, splittable set of results.
@garn/ui/paginationOn this page
Default
Previous · 1 2 3 · ellipsis · Next, page 2 active.
Installation
garn is copy-in — the garn CLI writes this component's source into your project, so you own and can edit it. It resolves any base files and installs the npm dependencies below for you.
npx garn-ui add pagination- Registry
pagination- Deps
lucide-react- Registry deps
buttonutils
Import
import { Pagination, PaginationContent, PaginationItem, PaginationLink, PaginationPrevious, PaginationNext, PaginationEllipsis } from "@garn/ui/pagination";Anatomy
The parts this component is built from — compose them to assemble it.
PaginationRequired[data-slot="pagination"]The <nav role="navigation" aria-label="pagination"> landmark.
PaginationContentRequired[data-slot="pagination-content"]The list of page items (a <ul>).
PaginationItemRequired[data-slot="pagination-item"]One list item wrapping a link or control.
PaginationLink[data-slot="pagination-link"]A page-number link; active marks the current page (aria-current="page" + a data-active reflect). Styled via buttonVariants.
PaginationPrevious[data-slot="pagination-previous"]Labeled "previous page" link.
PaginationNext[data-slot="pagination-next"]Labeled "next page" link.
PaginationEllipsis[data-slot="pagination-ellipsis"]Collapsed-range indicator; aria-hidden with an sr-only "More pages".
Props
The component's public props and their types.
The root has no props of its own — it's a thin wrapper over its native element. Its parts carry the API:
PaginationLink
active- Description
- Marks the current page — `aria-current="page"` + a `data-active` reflect.
- Type
boolean
size- Description
- Button size rung the link borrows for its box.
- Type
"icon" | "icon-lg" | "icon-sm" | "icon-xl" | "icon-xs" | "lg" | "md" | "sm" | "xl" | "xs"- Default
"icon"
Plus 288 inherited native HTML attributes.
Styling
Target these data-slots and states, and remap these tokens, to restyle without forking the component.
[data-slot="pagination"]| Property | Token | Tier |
|---|---|---|
itemGap | --garn-gap-inline | semantic · air |
ellipsisSize | --garn-control-h-md | semantic · air |
When to use
Split a long, ordered result set into pages and let users move between them.
Reach for something else when
- A continuous feed (use infinite scroll / load-more)
- a handful of items that fit on one screen
- a linear wizard (use a stepper).
Overview
Pagination splits a long, ordered result set into pages and lets users move between them. Pagination is the nav landmark, PaginationContent the list, PaginationLink each page number (active sets aria-current), with PaginationPrevious/PaginationNext controls. Use it when users benefit from stable, addressable pages (search results, tables); for continuously-growing feeds, infinite scroll or a "load more" button often fits better.
Guidelines
Choose pagination when position and addressability matter. Numbered pages let users bookmark, jump, and sense the set's size — ideal for results and data tables. For exploratory, endless feeds, consider load-more/infinite scroll instead.
Mark the current page and disable the ends. active carries aria-current="page"; disable Previous on page one and Next on the last page rather than letting them dead-end. Show enough context (first/last, neighbors, an ellipsis) without a giant row of numbers.
Keep it a labelled, keyboard-navigable nav. It's a navigation landmark — every control needs an accessible name ("Go to page 3", "Next page"). Pair it with a clear "N–M of T" indicator so users know where they are in the set.
Best practices
- Mark the current page's link with
active(sets aria-current="page" and data-active). - Collapse long ranges with PaginationEllipsis rather than rendering every number.
- Wrap your router's Link via PaginationLink for real client navigation.
- Don't render every page number for huge ranges — collapse the middle.
- Don't show pagination for a single page of results.
- Don't leave the boundary ambiguous — convey when Previous/Next are unavailable.
Content guidelines
- Label Previous/Next clearly and convey when they're unavailable.
- Keep page numbers plain; for huge ranges, collapse the middle with an ellipsis.
Troubleshooting
Screen readers don't announce which page is current.
Cause. No PaginationLink was given active.
Fix. Pass active to the current page's link so it sets aria-current="page".
Accessibility
- Role
navigation
Keyboard
| Tab | Move between page links and the previous/next controls. |
| Enter | Follow the focused page link. |
- The root <nav> is labeled "pagination" so it's a distinct landmark.
- The active page link sets aria-current="page" (via `active`).
- Previous/Next carry their own aria-labels; the ellipsis is aria-hidden with an sr-only label.