Components
Pagination
View in FigmaPresentational pagination primitives for URL-owned page links, previous/next controls, and ellipses.
Install
npx shadcn@latest add @alix/paginationPreview
Usage
<Pagination>
<PaginationContent>
<PaginationItem>
<PaginationPrevious href="/insights?page=4" />
</PaginationItem>
<PaginationItem>
<PaginationLink href="/insights?page=5" aria-label="Go to page 5" isActive>
5
</PaginationLink>
</PaginationItem>
<PaginationItem>
<PaginationNext href="/insights?page=6" />
</PaginationItem>
</PaginationContent>
</Pagination>Examples
01
Compact page range
Use only the local window of pages when the full result set is small enough to avoid ellipses.
<Pagination>
<PaginationContent>
<PaginationItem>
<PaginationPrevious href="/insights?page=1" />
</PaginationItem>
{[1, 2, 3].map((page) => (
<PaginationItem key={page}>
<PaginationLink
href={`/insights?page=${page}`}
aria-label={`Go to page ${page}`}
isActive={page === 2}
>
{page}
</PaginationLink>
</PaginationItem>
))}
<PaginationItem>
<PaginationNext href="/insights?page=3" />
</PaginationItem>
</PaginationContent>
</Pagination>02
Custom control labels
Previous and next labels can match the content model while keeping the same accessible names.
<Pagination>
<PaginationContent>
<PaginationItem>
<PaginationPrevious href="/insights?page=1" text="Newer" />
</PaginationItem>
<PaginationItem>
<PaginationLink href="/insights?page=2" aria-label="Go to page 2" isActive>
2
</PaginationLink>
</PaginationItem>
<PaginationItem>
<PaginationNext href="/insights?page=3" text="Older" />
</PaginationItem>
</PaginationContent>
</Pagination>03
Framework link render slot
Pass a rendered link element when a router needs to own navigation while Pagination owns state styling.
<Pagination>
<PaginationContent>
<PaginationItem>
<PaginationPrevious render={<a href="/services?page=1" />} />
</PaginationItem>
<PaginationItem>
<PaginationLink
aria-label="Go to page 2"
render={<a href="/services?page=2" />}
isActive
>
2
</PaginationLink>
</PaginationItem>
<PaginationItem>
<PaginationNext render={<a href="/services?page=3" />} />
</PaginationItem>
</PaginationContent>
</Pagination>04
Table footer navigation
Panel frames the dataset, DataTable owns rows, and Pagination sits in the footer for URL-owned pages.
Insight queue
Editorial review
| Insight | Owner | Status |
|---|---|---|
| Turnaround outlook | A. Ward | Ready |
| Working capital signals | M. Khan | Review |
<Panel
title="Insight queue"
description="Editorial review"
flush
footer={
<Pagination>
<PaginationContent>
<PaginationItem>
<PaginationPrevious href="/insights?page=1" />
</PaginationItem>
<PaginationItem>
<PaginationLink href="/insights?page=2" aria-label="Go to page 2" isActive>
2
</PaginationLink>
</PaginationItem>
<PaginationItem>
<PaginationNext href="/insights?page=3" />
</PaginationItem>
</PaginationContent>
</Pagination>
}
>
<DataTable
caption="Insight queue"
dense
rows={[
{ title: "Turnaround outlook", owner: "A. Ward", status: "Ready", tone: "success" as const },
{ title: "Working capital signals", owner: "M. Khan", status: "Review", tone: "warning" as const },
]}
columns={[
{ key: "title", header: "Insight", cell: (row) => row.title, cellClassName: "font-medium" },
{ key: "owner", header: "Owner", cell: (row) => row.owner },
{ key: "status", header: "Status", cell: (row) => <Badge variant={row.tone}>{row.status}</Badge> },
]}
/>
</Panel>When to use it
Use it for
- URL-owned result pages for insights, people, posts, or dashboard ledgers.
- Current page state where aria-current and a visible active treatment are required.
- Panel footers when a table or card list spans multiple server-rendered pages.
Reach for something else
- Hierarchy/path context for the current page — use Breadcrumbs instead.
- Switching views without changing the URL — use Tabs instead.
- Infinite or cursor-loaded feeds — use Button for a load-more action instead.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| PaginationLink.isActive | boolean | false | Marks the current page with aria-current and the active visual style. |
| PaginationLink.aria-label | string | — | Required accessible name, such as "Go to page 5", because the visible label is only a number. |
| render | ReactElement | — | Optional framework link slot; pagination state is merged onto the rendered element. |
| PaginationPrevious / PaginationNext.text | string | "Previous" / "Next" | Visible label beside the chevron on wider screens. |