A React pagination control - prev/next arrows and a live range readout - for tables and search results.
Pagination preview
Install Pagination
1. New project? One command installs the package and wires everything - see Installation:
pnpm dlx zyncat-ui init2. Import the component - it loads its own stylesheet:
import { Pagination } from '@zyncat/ui/pagination';Usage
import { Pagination } from '@zyncat/ui/pagination'; <Pagination range={[offset + 1, Math.min(offset + pageSize, total)]} total={total} hasPrev={offset > 0} hasNext={offset + pageSize < total} onPrev={() => setOffset(offset - pageSize)} onNext={() => setOffset(offset + pageSize)}/>Compose Pagination in your application. No Tailwind or external styling library is required; every value resolves from Zyncat UI's token vocabulary.
Pagination props
Pagination
Frequently asked questions
Splitting a long result set into pages so only one slice renders at a time, instead of the whole list at once. This component renders that control as a compact strip - a live "from-to of total" range readout next to a previous/next arrow pair - rather than a row of clickable page numbers.
Pass Pagination its own state, separate from the table: range is the [from, to] slice currently shown, total is the full row count (or omit it for an endless list), and hasPrev/hasNext gate the arrows. It is fully controlled - there is no internal page index - so onPrev/onNext just update whatever state feeds your rows, whether that is an offset, a page number or an opaque cursor.
Yes - total is optional (number | null), and hasPrev/hasNext are booleans rather than a page count, so the props map directly onto a cursor response: set them from whatever has_more or has_previous flags the API returns, and skip total entirely when the API does not provide one. Nothing in the component assumes a fixed page size or a known page count.
Set loading - both arrows go inert and the one that was just clicked (tracked internally) shows the Button spinner while the other simply disables, so the control never reflows or lets a second click queue up mid-fetch.
Yes to both. The root is a nav element with an aria-label landmark (name the list, e.g. "Posts", not the word "pagination"), the range readout is aria-live="polite" so screen readers announce new totals, and each arrow carries its own aria-label plus native button semantics. It ships with "use client" intact for the Next.js App Router, has zero runtime dependencies, and the slide animation on the range readout collapses under prefers-reduced-motion.