Pagination
A page navigation component with smart ellipsis truncation, previous/next buttons, and a compact variant.
Basic
A flexible pagination component for navigating paged data sets. Features smart ellipsis truncation, previous/next buttons, configurable sibling counts, and a compact variant.
Installation
Install the component via the CLI in one command.
npx @work-rjkashyap/unified-ui add paginationpnpm dlx @work-rjkashyap/unified-ui add paginationnpx @work-rjkashyap/unified-ui add paginationbunx @work-rjkashyap/unified-ui add paginationIf you haven't initialized your project yet, run npx @work-rjkashyap/unified-ui init first. See the CLI docs for details.
Or install the full package
Use this approach if you prefer to install the entire design system as a dependency instead of copying individual components.
npm install @work-rjkashyap/unified-uipnpm add @work-rjkashyap/unified-uiyarn add @work-rjkashyap/unified-uibun add @work-rjkashyap/unified-uiAnatomy
import {
Pagination,
Table,
TableHeader,
TableBody,
TableRow,
TableHead,
TableCell,
} from "@work-rjkashyap/unified-ui";Basic Usage
The Pagination component renders page number buttons with smart ellipsis truncation for large page counts. Previous and Next buttons are automatically disabled at the boundaries.
<Pagination totalPages={20} />Controlled
Use page and onPageChange for controlled pagination state.
const [page, setPage] = useState(1);
<Pagination
page={page}
totalPages={20}
onPageChange={setPage}
/>
<p>Current page: {page}</p>Uncontrolled
Use defaultPage to set the initial page without controlling state externally. The component manages its own internal state.
<Pagination totalPages={20} defaultPage={5} />Variants
Default
The standard pagination with individual page number buttons, ellipsis, and prev/next controls.
Compact
A minimal "Page X of Y" display with just the prev/next buttons. Ideal for mobile or space-constrained layouts.
Sizes
Two sizes are available for different density needs.
| Size | Button Height | Use Case |
|---|---|---|
sm | 32px | Dense UIs, admin tables, mobile |
md | 36px | Default, general-purpose tables |
Smart Ellipsis
The pagination component uses a smart truncation algorithm that shows:
- The first and last page numbers (boundary pages)
- A configurable number of sibling pages around the current page
- Ellipsis (
…) indicators when pages are skipped
Sibling and Boundary Count
Configure how many sibling pages appear around the current page and how many boundary pages appear at the edges.
{
/* Default: 1 sibling on each side */
}
<Pagination totalPages={50} defaultPage={25} />;
{
/* More siblings for wider navigation */
}
<Pagination totalPages={50} defaultPage={25} siblingCount={2} />;
{
/* More boundary pages */
}
<Pagination totalPages={50} defaultPage={25} boundaryCount={2} />;| Prop | Default | Description |
|---|---|---|
siblingCount | 1 | Number of pages to show on each side of the current page. |
boundaryCount | 1 | Number of pages to always show at the start and end. |
Disabled State
The previous button is automatically disabled on page 1, and the next button is disabled on the last page. You can also disable the entire component.
{
/* Auto-disabled at boundaries */
}
<Pagination totalPages={10} defaultPage={1} />;
{
/* Fully disabled */
}
<Pagination totalPages={10} defaultPage={5} disabled />;Small Page Counts
When the total page count is small enough to show all pages, no ellipsis is rendered.
Table Integration
A common pattern pairing pagination with a data table.
import {
Table,
TableHeader,
TableBody,
TableRow,
TableHead,
TableCell,
Pagination,
} from "@work-rjkashyap/unified-ui";
function DataTable({ data, pageSize = 10 }) {
const [page, setPage] = useState(1);
const totalPages = Math.ceil(data.length / pageSize);
const pageData = data.slice((page - 1) * pageSize, page * pageSize);
return (
<div className="flex flex-col gap-4">
<Table>
<TableHeader>
<TableRow>
<TableHead>Name</TableHead>
<TableHead>Email</TableHead>
<TableHead align="right">Role</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{pageData.map((row) => (
<TableRow key={row.id}>
<TableCell>{row.name}</TableCell>
<TableCell>{row.email}</TableCell>
<TableCell align="right">{row.role}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
<div className="flex justify-center">
<Pagination
page={page}
totalPages={totalPages}
onPageChange={setPage}
/>
</div>
</div>
);
}Compact Variant with Info
Combine the compact variant with additional page info for a mobile-friendly layout.
<div className="flex items-center justify-between w-full">
<p className="text-sm text-muted-foreground">
Showing {startItem}–{endItem} of {totalItems}
</p>
<Pagination
page={page}
totalPages={totalPages}
onPageChange={setPage}
variant="compact"
size="sm"
/>
</div>Props
| Prop | Type | Default | Description |
|---|---|---|---|
page | number | — | Controlled current page (1-indexed). |
defaultPage | number | 1 | Default page for uncontrolled mode. |
totalPages | number | required | Total number of pages. |
onPageChange | (page: number) => void | — | Callback when the page changes. |
variant | "default" | "compact" | "default" | Visual variant. "compact" shows "Page X of Y". |
size | "sm" | "md" | "md" | Size of the pagination buttons. |
siblingCount | number | 1 | Number of pages shown on each side of the current page. |
boundaryCount | number | 1 | Number of pages always shown at the start and end. |
disabled | boolean | false | Disables the entire pagination component. |
className | string | — | Additional CSS classes on the root <nav> element. |
Accessibility
The Pagination component renders as a nav element with
aria-label="Pagination" for proper landmark identification by screen
readers.
- Semantic markup — Renders as
<nav>witharia-label="Pagination". - Current page — The active page button has
aria-current="page"so screen readers announce which page is selected. - Disabled buttons — Previous/Next buttons at the boundaries use
aria-disabled="true"and are not focusable. - Page labels — Each page button has an accessible label like "Go to page 5" for screen readers.
- Ellipsis — Rendered as
<span>witharia-hidden="true"since they're purely visual. - Focus ring — Visible on keyboard navigation via
focus-visiblewith the compact focus ring utility. - Keyboard navigation — All buttons are standard
<button>elements, navigable withTaband activated withEnterorSpace. - The component does not trap focus — it participates in the normal tab order.
Design Tokens
| Token | Usage |
|---|---|
--primary | Active page button background |
--primary-foreground | Active page button text |
--muted | Inactive page button hover background |
--foreground | Page button text |
--muted-foreground | Ellipsis and disabled button text |
--border | Button border color |
--radius-md | Button border radius |
--duration-fast | Hover/focus transition speed |