Numbered navigation for splitting long lists across pages.
Description
Pagination is a compound component that renders a <nav role="navigation" aria-label="pagination"> containing numbered links and direction controls. It is composed of PaginationContent, PaginationLink, PaginationEllipsis, and four directional buttons (PaginationFirst, PaginationPrevious, PaginationNext, PaginationLast). The compact-grouped variant delegates to ButtonGroup for a single connected surface; the compact variant lays the controls out as standalone buttons with gaps.
Reach for it on long tables, search results, archives, and report views where the user needs to scan ranges and jump to specific pages. Pair it with a row-count or page-size selector when the surface has room. The visible page set is yours to decide: a typical pattern is first, a window around the current page, an ellipsis, and last.
Don't use Pagination for infinite-scroll feeds (no controls needed), for switching between sibling panels (use Tabs), or for hierarchical trails like Home / Library / Albums (use Breadcrumb). A single back / forward pair without numbered pages is better served by two Buttons.
Installation
Anatomy
Usage
Examples
Variants
variant picks between two layouts. compact-grouped (default) delegates to ButtonGroup, fusing every control into one connected surface with shared borders. compact renders the same controls as standalone buttons separated by small gaps, useful when the pagination sits on a busy surface where the grouped look feels heavy.
Shape
shape controls corner radius on both variants. rounded (default) follows the size scale; pill makes every control fully rounded. pill reads well on cards and toolbars; rounded fits table footers and dense layouts.
With Jump Buttons
Add PaginationFirst and PaginationLast at the ends when the range is long enough that jumping to the extremes saves clicks. For short ranges (under ~5 pages) the jump buttons are overkill; rely on PaginationPrevious and PaginationNext alone.
Disabled at Boundaries
Pass the native disabled attribute to mark directional buttons as unavailable. The typical pattern: disable PaginationFirst and PaginationPrevious on page 1, and PaginationNext and PaginationLast on the last page. Visuals (pointer, color, focus) come from the underlying button.
Controlled
Drive the current page from a useState and pass isActive to the matching PaginationLink. Each control wires its own onClick to move state, and the directional buttons clamp to 1 and total. Build your visible page window however you want; a 1 ... n-1 n n+1 ... total pattern covers most table views.
Composition with Router Link
Every clickable part accepts asChild, so you can hand off rendering to a router link (<a href>, next/link, react-router's Link) without losing the styling or ARIA wiring. When using asChild on PaginationLink, set aria-current="page" on the inner anchor for the active page so it still announces correctly.
Accessibility
Pagination renders a real <nav> with aria-label="pagination", so screen readers announce it as a navigation landmark. Each directional button (First, Previous, Next, Last) ships with a built-in aria-label. The active PaginationLink writes aria-current="page" automatically when isActive is set, and PaginationEllipsis is marked role="presentation" with a paired .sr-only "More pages" hint.
ARIA notes:
- The root sets
role="navigation"andaria-label="pagination"; do not wrap it in another nav landmark. - The active page is identified with
aria-current="page". When usingasChildonPaginationLink, mirror that attribute on the inner anchor so it survives the slot swap. PaginationFirst,PaginationPrevious,PaginationNext, andPaginationLastset descriptivearia-labels ("Go to first page", etc.). Override with your ownaria-labelfor localized copy.- For long, dynamic ranges, announce page transitions on the page region the pagination controls (e.g. set
aria-live="polite"on the result list), not on the pagination itself.
Styling
Tailwind override: pass className to merge Tailwind classes with the component's CVA classes (via cn()):
Data slots and attributes: the component sets these for CSS targeting:
data-slot="pagination"on the root<nav>, withdata-variant="<variant>"anddata-shape="<shape>".data-slot="pagination-content"on the inner wrapper (forwarded toButtonGroupwhenvariant="compact-grouped").data-slot="pagination-link"on numbered links, withdata-activewhenisActiveis set.data-slot="pagination-first",data-slot="pagination-previous",data-slot="pagination-next",data-slot="pagination-last"on the matching directional buttons.data-slot="pagination-ellipsis"on the truncation marker.
Target a specific state in CSS:
Related Components
- ButtonGroup: the primitive that the
compact-groupedvariant delegates to; use it directly for non-numbered segmented controls. - Tabs: use this when the controls switch between sibling content panels in place rather than paging through a list.
- Breadcrumb: use this for hierarchical location trails, not paged sequences.
- Button: use a single Prev / Next pair (two
Buttons) when there are no numbered pages to render.
API Reference
Pagination
The root container. Renders a <nav role="navigation" aria-label="pagination">, broadcasts variant and shape to every part through context, and writes data-slot="pagination" plus data-variant and data-shape. Extends React.ComponentProps<"nav">, so any standard <nav> attribute (id, role, aria-*, onClick, etc.) is accepted.
Props
Variants
PaginationContent
The inner wrapper that holds the controls. When variant="compact-grouped", this renders a ButtonGroup with variant="soft", size="md", and the shape inherited from the root. Otherwise it renders a <div> with inline-flex items-center gap-1. Writes data-slot="pagination-content". Extends React.ComponentProps<"div">.
Props
PaginationLink
A numbered page link. Renders as a <button> by default; pass asChild to swap in an anchor or router link. Writes data-slot="pagination-link", data-active when isActive is set, and aria-current="page" for the active page. Extends Omit<React.ComponentProps<"button">, "type">.
Props
PaginationFirst
Directional button that jumps to the first page. Ships with aria-label="Go to first page" and a built-in RiArrowLeftDoubleFill icon. Override the icon by passing children. Writes data-slot="pagination-first". Extends Omit<React.ComponentProps<"button">, "type">.
Props
PaginationPrevious
Directional button that steps one page back. Ships with aria-label="Go to previous page" and a RiArrowLeftSLine icon. Writes data-slot="pagination-previous". Extends Omit<React.ComponentProps<"button">, "type">.
Props
PaginationNext
Directional button that steps one page forward. Ships with aria-label="Go to next page" and a RiArrowRightSLine icon. Writes data-slot="pagination-next". Extends Omit<React.ComponentProps<"button">, "type">.
Props
PaginationLast
Directional button that jumps to the last page. Ships with aria-label="Go to last page" and a RiArrowRightDoubleFill icon. Writes data-slot="pagination-last". Extends Omit<React.ComponentProps<"button">, "type">.
Props
PaginationEllipsis
Non-interactive truncation marker. Renders a <span role="presentation" aria-hidden="true"> containing the visible glyph and an .sr-only "More pages" hint. Writes data-slot="pagination-ellipsis". Extends React.ComponentProps<"span">.