Pagination
A row of page links for moving through a long list or a set of results. The page links, Previous and Next are all Buttons, in three variants, two shapes and four sizes, the same as the Figma set.
Installation
npx @smicolon/fasla-ui@latest add paginationRun init once per project before adding components. See Installation.
Pagination is built on Button, so adding it adds button too.
Preview
Variants
variant picks the Button type of each part. default has ghost page links and a solid current page. solid has soft page links and a solid current page. outlined has outline page links, a soft current page, and outline Previous and Next. In the other two, Previous and Next are ghost.
defaultsolidoutlinedSizes
The four Button sizes: xs is 26px tall, sm 32px, md 36px and lg 40px. A page link is a square of that height, and its number is 14px at every size. A number too wide for its square, such as 100 at xs, widens it.
xssmmdlgShape
shape="square" gives every part the Button's standard 8px corners, and shape="rounded" its full radius: circles for the page links, pills for Previous and Next. The focus ring follows the shape.
defaultsolidoutlinedPrevious and Next
PaginationPrevious and PaginationNext show a label and a chevron. The label follows the page's language, Previous and Next or السابق and التالي; pass text to change it. With iconOnly they show the chevron alone in a square, and keep the label for screen readers. Disable Previous on the first page and Next on the last with disabled.
Long ranges
Show the first and last pages, the current page with one on each side, and put PaginationEllipsis where pages are left out: 1 … 5 6 7 … 20. The ellipsis is a square the size of a page link, but not a link, and screen readers hear “More pages”. Icon-only Previous and Next keep a long row short. On a phone, show fewer pages around the current one, as this example does.
Accessibility
Paginationis anavlandmark. Name it in the page's language witharia-label, as the examples here do; otherwise it is called Pagination. Give each one on a page its own name.- The current page carries
aria-current="page", so screen readers announce it as the current page. - Previous and Next are named by their label, which stays readable when
iconOnlyhides it. The chevrons are hidden from assistive technology. - The ellipsis takes no focus. Its glyph is hidden, and screen readers hear “More pages”, or “مزيد من الصفحات” when the nearest
langis Arabic. - A disabled link carries
aria-disabled, leaves the tab order and ignores clicks. It stays a link, so screen readers still find it. - Every part is a Button, so keyboard focus draws the Button's 2px ring, 2px outside the link.
- Direction follows the page's
dir; there is no prop. Right to left, the row starts on the right with Previous, and each chevron turns to point outward. Page numbers stay in Western digits.
Props
Pagination
| Prop | Type | Default | Description |
|---|---|---|---|
variant | "default" | "solid" | "outlined" | "default" | The Button type of each part: ghost, soft or outline page links. |
shape | "square" | "rounded" | "square" | The corners of every part: the Button's standard radius, or its full radius. |
size | "xs" | "sm" | "md" | "lg" | "md" | The height of every part, from 26px to 40px. |
aria-label | string | "Pagination" | The landmark's name for screen readers, in the page's language. |
children | ReactNode | — | A PaginationContent. |
className | string | — | Extra classes for the nav element, a centred row by default. |
PaginationContent
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | — | The PaginationItems, in reading order. |
className | string | — | Extra classes for the ul element. It wraps onto a second row rather than overflow. |
PaginationItem
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | — | One page link, Previous, Next or the ellipsis. |
className | string | — | Extra classes for the li element. |
PaginationLink
| Prop | Type | Default | Description |
|---|---|---|---|
href | string | — | Where the link goes. The link takes every other attribute of an a element too. |
isActive | boolean | false | Marks the current page: the current-page look and aria-current="page". |
disabled | boolean | false | Dims the link, takes it out of the tab order and ignores clicks. |
asChild | boolean | false | Makes its only child the link, with these styles: Next.js's Link, say, or a button when paging changes state rather than the URL. |
children | ReactNode | — | The page number, in Western digits on Arabic pages too. |
className | string | — | Extra classes for the link. |
PaginationPrevious, PaginationNext
| Prop | Type | Default | Description |
|---|---|---|---|
href | string | — | Where the link goes. |
text | ReactNode | — | The label. Previous or Next by default, in the page's language. |
iconOnly | boolean | false | Shows the chevron alone, in a square. The label stays for screen readers. |
disabled | boolean | false | Dims the link, takes it out of the tab order and ignores clicks. |
asChild | boolean | false | Makes its only child the link, and puts the label and chevron inside it. |
className | string | — | Extra classes for the link. |
PaginationEllipsis
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | — | Extra classes for the ellipsis. Its screen-reader text is built in. |
Usage
Links to pages
import {
Pagination,
PaginationContent,
PaginationItem,
PaginationLink,
PaginationNext,
PaginationPrevious,
} from "@/components/ui/pagination"
export function OrdersPagination() {
return (
<Pagination aria-label="Pagination">
<PaginationContent>
<PaginationItem>
<PaginationPrevious href="?page=1" />
</PaginationItem>
<PaginationItem>
<PaginationLink href="?page=1">1</PaginationLink>
</PaginationItem>
<PaginationItem>
<PaginationLink href="?page=2" isActive>
2
</PaginationLink>
</PaginationItem>
<PaginationItem>
<PaginationLink href="?page=3">3</PaginationLink>
</PaginationItem>
<PaginationItem>
<PaginationNext href="?page=3" />
</PaginationItem>
</PaginationContent>
</Pagination>
)
}A long range
import {
Pagination,
PaginationContent,
PaginationEllipsis,
PaginationItem,
PaginationLink,
PaginationNext,
PaginationPrevious,
} from "@/components/ui/pagination"
const PAGES = [1, null, 5, 6, 7, null, 20]
export function ArticlesPagination() {
return (
<Pagination aria-label="Pagination" variant="outlined">
<PaginationContent>
<PaginationItem>
<PaginationPrevious href="?page=5" iconOnly />
</PaginationItem>
{PAGES.map((page, i) => (
<PaginationItem key={i}>
{page === null ? (
<PaginationEllipsis />
) : (
<PaginationLink href={`?page=${page}`} isActive={page === 6}>
{page}
</PaginationLink>
)}
</PaginationItem>
))}
<PaginationItem>
<PaginationNext href="?page=7" iconOnly />
</PaginationItem>
</PaginationContent>
</Pagination>
)
}Paging with state
"use client"
import { useState } from "react"
import {
Pagination,
PaginationContent,
PaginationItem,
PaginationLink,
PaginationNext,
PaginationPrevious,
} from "@/components/ui/pagination"
const TOTAL = 5
export function TablePagination() {
const [page, setPage] = useState(1)
return (
<Pagination aria-label="Pagination" size="sm">
<PaginationContent>
<PaginationItem>
<PaginationPrevious asChild disabled={page === 1}>
<button type="button" onClick={() => setPage(page - 1)} />
</PaginationPrevious>
</PaginationItem>
{Array.from({ length: TOTAL }, (_, i) => i + 1).map((n) => (
<PaginationItem key={n}>
<PaginationLink asChild isActive={n === page}>
<button type="button" onClick={() => setPage(n)}>
{n}
</button>
</PaginationLink>
</PaginationItem>
))}
<PaginationItem>
<PaginationNext asChild disabled={page === TOTAL}>
<button type="button" onClick={() => setPage(page + 1)} />
</PaginationNext>
</PaginationItem>
</PaginationContent>
</Pagination>
)
}With the Next.js Link
import Link from "next/link"
import {
Pagination,
PaginationContent,
PaginationItem,
PaginationLink,
PaginationNext,
} from "@/components/ui/pagination"
export function ResultsPagination() {
return (
<Pagination aria-label="Pagination" shape="rounded">
<PaginationContent>
<PaginationItem>
<PaginationLink asChild isActive>
<Link href="/results?page=1">1</Link>
</PaginationLink>
</PaginationItem>
<PaginationItem>
<PaginationLink asChild>
<Link href="/results?page=2">2</Link>
</PaginationLink>
</PaginationItem>
<PaginationItem>
<PaginationNext asChild>
<Link href="/results?page=2" />
</PaginationNext>
</PaginationItem>
</PaginationContent>
</Pagination>
)
}