Skip to content

Navigation

Pagination

Move through a result set that already knows its size, with the page sizes people actually use.

Examples

A summary and the pages

The row count belongs beside the controls: the reader answers "is the thing I want on this page" before deciding whether to move.

61–80 of 480 rows

Compact, for a footer

Arrows and a status: the shape a card footer or a mobile toolbar has room for, with both end jumps still one click away.

Prepared locally · 480 rows

Neighbours, and the end jumps

`siblings` widens the window; `edges` is the pair of double chevrons that jumps to either end. The page numbers themselves are derived — never a list you maintain.

siblings = 2

edges = false

At the ends, and when the total changes

The first page, the last page, and a set that fits on one: the arrows disable themselves and the window collapses to the pages that exist. Lower the page count while you are deep in the set and the position clamps instead of pointing at a page that is gone.

Page count

First page

Last page

Usage

import { Pagination, PaginationSummary } from "@luc/ui";
<Pagination page={page} pageCount={24} onPageChange={setPage} />

1-based `page` and a `pageCount`; everything else — the window, the gaps, the disabled arrows — is derived.

API

Read from the published TypeScript API, so a prop that is not listed here does not exist and a default that is listed is the one the compiler sees.

`paginationWindow` is exported and pure because the window *is* the component: a caller that needs to know which pages are on screen — to map a keyboard shortcut, or to prefetch — should call it rather than write a second version of the rule.

Exported symbols
ExportKindSignature
Pagination9 documented props({ className, page, pageCount, siblings, edges, compact, onPageChange, label, previousLabel, nextLabel, ...props }: PaginationProps) => import("@types/react").JSX.Element
PaginationSummary4 documented props({ className, from, to, total, unit, ...props }: PaginationSummaryProps) => import("@types/react").JSX.Element
paginationWindowexport(page: number, pageCount: number, siblings?: number) => PageToken[]

Pagination props

Type: PaginationProps

Values the compiler reads from the declaration.

Pagination props
PropTypeDefaultNotes
compact?boolean | undefined—Replaces the window with arrow controls only — for a narrow container or a mobile toolbar.
edges?boolean | undefined—Show first/last controls. Default `true`.
label?string | undefined—Accessible name. Every pagination landmark needs one.
nextLabel?string | undefined—
onPageChange?((page: number) => void) | undefined—
pagenumber—Current page, 1-based.
pageCountnumber—Total pages. `0` and `1` both render a single, current page.
previousLabel?string | undefined—Text used inside the "previous" control; hidden below `sm`.
siblings?number | undefined—Neighbours shown on each side of the current page. Default 1.

PaginationSummary props

Type: PaginationSummaryProps

Values the compiler reads from the declaration.

PaginationSummary props
PropTypeDefaultNotes
fromnumber—First row number on the current page, 1-based.
tonumber—Last row number on the current page, 1-based.
totalnumber—Total rows.
unit?string | undefined—Noun for the rows. Default "results".

Guidelines

Do

  • Let the component derive the window: `siblings` is the only knob a breakpoint needs, and a hand-built page array cannot re-derive itself for a narrower container.
  • Name the landmark for the collection it pages (`label="Trades"`), and put the row summary next to the controls with `PaginationSummary`.
  • Reach for `compact` in a card footer or a mobile toolbar, where the arrows and the `12 / 24` status are the whole control.

Don't

  • Do not re-derive the window yourself to predict which pages are on screen — `paginationWindow` is exported and pure, and two implementations of one rule will disagree.
  • Do not hide the pager when there is only one page: a stable footer is worth more than four saved pixels, and the layout moving on the first search result is worse.
  • Do not use it to scroll a region: paging a result set and scrolling a container are different jobs, and the second one is `ScrollArea`.

Accessibility

The control is a named `nav` and the current page is the only element carrying `aria-current="page"`; every numbered control has an accessible name (`Page 24`), the first/last arrows are disabled at the ends they cannot move from, and the Previous/Next words are hidden below `sm` while their icons keep the names. `PaginationSummary` groups digits with a fixed locale, so the server and the browser cannot render `1,284` two different ways.

Luc UI v1.0Paginationdark theme