Unified UI

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 pagination
pnpm dlx @work-rjkashyap/unified-ui add pagination
npx @work-rjkashyap/unified-ui add pagination
bunx @work-rjkashyap/unified-ui add pagination

If 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-ui
pnpm add @work-rjkashyap/unified-ui
yarn add @work-rjkashyap/unified-ui
bun add @work-rjkashyap/unified-ui

Anatomy

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.

SizeButton HeightUse Case
sm32pxDense UIs, admin tables, mobile
md36pxDefault, 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
Page 1 of 20
Page 10 of 20
Page 20 of 20

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} />;
PropDefaultDescription
siblingCount1Number of pages to show on each side of the current page.
boundaryCount1Number 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

PropTypeDefaultDescription
pagenumberControlled current page (1-indexed).
defaultPagenumber1Default page for uncontrolled mode.
totalPagesnumberrequiredTotal number of pages.
onPageChange(page: number) => voidCallback 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.
siblingCountnumber1Number of pages shown on each side of the current page.
boundaryCountnumber1Number of pages always shown at the start and end.
disabledbooleanfalseDisables the entire pagination component.
classNamestringAdditional 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> with aria-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> with aria-hidden="true" since they're purely visual.
  • Focus ring — Visible on keyboard navigation via focus-visible with the compact focus ring utility.
  • Keyboard navigation — All buttons are standard <button> elements, navigable with Tab and activated with Enter or Space.
  • The component does not trap focus — it participates in the normal tab order.

Design Tokens

TokenUsage
--primaryActive page button background
--primary-foregroundActive page button text
--mutedInactive page button hover background
--foregroundPage button text
--muted-foregroundEllipsis and disabled button text
--borderButton border color
--radius-mdButton border radius
--duration-fastHover/focus transition speed

On this page