// preview in
saved for every page

Pagination

Moves through a long list one page at a time and shows where people are in it. Page numbers are links for pages a search engine should find, or buttons for a list that lives in the browser.

Pattern WAI-ARIA Landmark (navigation)Client componentSource ↗

Installation

Set up Camaleo once (Installation), then import what you need. DataTable already includes it; use it on its own for any other list.

import { Pagination } from "@camaleo/react";

Anatomy

A nav landmark around a list. The first and last items are Previous and Next; between them, the page numbers and the gaps. The number of items stays the same as the page changes, so the control doesn't jump in width.

Examples

Every example is a real file you can copy. They all wear the skin, mode and brand from the bar above.

minimal · light · #3fa34d
Blog archive · Page 1 of 8

Links, for pages people can share

Pass getHref when each page has its own URL: search engines follow the links, and people can open a page in a new tab or share it.

Recent orders

  • #4821$49.90Maya ChenPaid
  • #4820$87.69Daniel OkaforPaid
  • #4819$125.48Sofia RamirezPending
  • #4818$163.27Liam BeckerPaid

A list in the browser

Change the page. With onPageChange the numbers are buttons; focus stays on the one you pressed, and a status message says "Page 3 of 10".

Trip photos · Page 12 of 24

Compact, for phones

With text arrows and one sibling, the row wraps to two lines below about 420px. siblings={0} and the arrows' names in VisuallyHidden keep it under 310px in every skin, one line on a 390px phone; the total moves to the text above.

Showing 21–40 of 312

With page size and a count

Change the rows per page. "Showing 21–40 of 312" says how much there is, and the new page keeps the first row you were looking at.

First and last always there

The first and last pages never leave the row, so a single press goes back to the start or straight to the end of 48 pages.

siblings=0
siblings=1
siblings=2

Siblings

siblings sets how many pages show on each side of the current one. Use 0 in narrow places, 2 when people tend to jump a few pages.

Names for screen readers

Translate aria-label, the arrows and pageLabel. Including the total in pageLabel makes each number read "Page 3 of 12".

In context

These are whole pieces of UI you can copy, in the skin you picked.

Blog archiveCard · Link · Badge · Pagination

Recent orders

OrderCustomerStatusTotal
#5132Maya ChenPaid$49.90
#5131Daniel OkaforPaid$87.69
#5130Sofia RamirezPending$125.48
#5129Liam BeckerPaid$163.27
#5128Aisha PatelPaid$201.06
Showing 1–5 of 312
Orders tableTable · Badge · NativeSelect · Pagination
14 articles match "refund"
  1. How to refund an orderFull refunds go back to the original payment method in 5 to 10 business days.
  2. Responding to a chargebackSend proof of delivery within 7 days so the bank can review the dispute.
  3. Partial refunds and store creditRefund part of an order or give credit the customer can use on their next purchase.
Search resultsSearchInput · Link · Pagination · VisuallyHidden

Guidelines

Showing 21–40 of 312
DoSay where people are and how much there is.

The current page and "Showing 21–40 of 312" answer both questions before anyone asks.

Don’tOnly Previous and Next.

Without a page number or a total, people can't tell how far they've gone or how much is left.

Team members
  • Ana Ribeiro
  • Carlos Mendes
  • Júlia Santos
  • Pedro Almeida
  • Beatriz Costa
DoShow a short list whole.

Five names fit on the screen. Reading them takes less time than finding the next page.

Team members
  • Ana Ribeiro
  • Carlos Mendes
Don’tPaginate a tiny list.

Three pages of two names hide most of the list behind clicks, and Ctrl+F stops finding them.

Props

PropTypeDefaultDescription
page*number—The current page, from 1.
count*number—Total number of pages.
siblingsnumber1Pages shown on each side of the current one. The first and last pages are always shown.
getHref((page: number) => string)—Renders links (crawlable, open in a new tab). Give this or onPageChange.
onPageChange((page: number) => void)—Renders buttons and is called with the chosen page, for client-side state.
previousLabelReactNode"Previous"Content of the previous-page control, after the chevron. Translate it.
nextLabelReactNode"Next"Content of the next-page control, before the chevron. Translate it.
pageLabel((page: number) => string)(page) => `Page ${page}`Accessible name of each page number. Translate it.

Other props go to the nav. Give either getHref (links) or onPageChange (buttons).

Styling hooks

Restyle it without forking: target the attributes, or set the tokens on a parent, a skin or the pagination itself.

Attributes

aria-current"page" on the current page
aria-disabled"true" on Previous at page 1 and Next at the last page

Tokens it reads

Color

--cml-border--cml-fg--cml-fg-muted--cml-focus-offset--cml-focus-ring--cml-focus-width--cml-surface-raised

Shape & size

--cml-border-width--cml-control-height--cml-radius-control

Type & motion

--cml-duration-fast--cml-ease-standard--cml-font-control--cml-font-size-control--cml-font-weight-control

This component only

--cml-corner-shape--cml-disabled-opacity--cml-item-highlight
/* square page numbers in the orders table */
.orders [data-slot="pagination-item"] {
  --cml-radius-control: 4px;
}

Accessibility

TabMoves to Previous, each page number and Next, in order. The … gaps are skipped.
Shift + TabMoves back through the same controls.
EnterGoes to the focused page. As buttons, Space does too.
Enter on Next at the last pageAs buttons, does nothing and focus stays on Next: it is aria-disabled, not disabled. As links, the edge control has no href and Tab skips it.
Was this page helpful? · Edit this page on GitHub