Pagination
A list footer that summarizes the visible range and steps between pages.
For tables, reach for DataTable.Pagination — the
ready-made instance of this pattern. Compose it by hand, as shown below, when
you're paginating something that isn't a table (for example, a cursor-based list).
When to use
Use pagination as the footer of a paged list or table: it tells people where they are in the full set and lets them move through it. Pair it with a list that loads one page at a time — not with a short, fully-loaded list where everything already fits on screen.
Anatomy
The pattern is a single row with two halves:
- Range summary (leading) —
Showing 1–20 of 1,240 <noun>. The visible range and the total, so the count is legible at a glance. - Page controls (trailing) — a Rows per page Select
and three icon Buttons: first page (
|<), previous (<), and next (>).
This hand-composed instance has no skip-to-last button, because it targets
forward-only cursor lists where an arbitrary last page can't be addressed reliably
(see Guidelines). Instances that do know their page count — like the
DataTable.Pagination table instance — can offer
one.
Usage
import { Button } from "@smartacteam/ambient-web/button";
import { IconArrowWallLeft } from "@smartacteam/ambient-web/icons/icon-arrow-wall-left";
import { IconChevronLeft } from "@smartacteam/ambient-web/icons/icon-chevron-left";
import { IconChevronRight } from "@smartacteam/ambient-web/icons/icon-chevron-right";
import { Select } from "@smartacteam/ambient-web/select";
import { TabularNum } from "@smartacteam/ambient-web/tabular-num";<div className="flex w-full flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
<p className="text-sm text-text-secondary">
Showing <TabularNum>{rangeStart.toLocaleString()}</TabularNum>–
<TabularNum>{rangeEnd.toLocaleString()}</TabularNum> of{" "}
<TabularNum>{total.toLocaleString()}</TabularNum> customers
</p>
<div className="flex items-center gap-0.5">
<div className="flex items-center gap-2">
<p className="text-sm text-text-secondary">Rows per page</p>
<Select.Root value={`${pageSize}`} onValueChange={onPageSizeChange}>
<Select.Trigger size="sm">
<Select.Value placeholder={pageSize} />
</Select.Trigger>
<Select.Content side="top">
{[10, 20, 30, 40, 50].map((size) => (
<Select.Item key={size} value={`${size}`}>
{size}
</Select.Item>
))}
</Select.Content>
</Select.Root>
</div>
<div className="flex items-center gap-0.5">
<Button
variant="ghost"
size="icon-sm"
onClick={onFirstPage}
disabled={!hasPreviousPage}
>
<span className="sr-only">Go to first page</span>
<IconArrowWallLeft />
</Button>
<Button
variant="ghost"
size="icon-sm"
onClick={onPreviousPage}
disabled={!hasPreviousPage}
>
<span className="sr-only">Go to previous page</span>
<IconChevronLeft />
</Button>
<Button
variant="ghost"
size="icon-sm"
onClick={onNextPage}
disabled={!hasNextPage}
>
<span className="sr-only">Go to next page</span>
<IconChevronRight />
</Button>
</div>
</div>
</div>Guidelines
- Wrap the numbers in
TabularNum. The range and total change as people page through; tabular figures keep the digits from shifting width as they update. - Localize the numbers. Use
toLocaleString()(or your i18n number formatter) so large totals read as1,240, not1240. - Source the values from the list, not the UI. The total feeds “of Y” from
the query's
totalCount;hasPreviousPage/hasNextPagecome from the cursorPageInfo— derive the disabled states from those, never from a guess at the page count. - Skip-to-last only when the last page is addressable. Forward-only cursors
have no cursor for an arbitrary last page, so this instance omits the control —
it would be a dead end or a lie. Instances backed by a known page count (a
client-side table) can include it. The first page is always reachable (the null
cursor), so
|<always stays. - Every icon button needs a label. The arrows are icon-only, so each carries
an
sr-onlyspan (“Go to first / previous / next page”) for screen readers. - Hide it when it can't do anything. If the whole list fits on one page, render nothing rather than a row of disabled controls.