Skip to content
UI

Pagination

Links that move between the pages of a long list.

@zenoxai/ui/components/pagination Markdown
<Pagination>
  <PaginationContent>
    <PaginationItem>
      <PaginationPrevious href="#" />
    </PaginationItem>
    <PaginationItem>
      <PaginationLink href="#">1</PaginationLink>
    </PaginationItem>
    <PaginationItem>
      <PaginationLink href="#" isActive>
        2
      </PaginationLink>
    </PaginationItem>
    <PaginationItem>
      <PaginationLink href="#">3</PaginationLink>
    </PaginationItem>
    <PaginationItem>
      <PaginationEllipsis />
    </PaginationItem>
    <PaginationItem>
      <PaginationLink href="#">13</PaginationLink>
    </PaginationItem>
    <PaginationItem>
      <PaginationNext href="#" />
    </PaginationItem>
  </PaginationContent>
</Pagination>

Usage

import {
  Pagination,
  PaginationContent,
  PaginationItem,
  PaginationLink,
  PaginationNext,
  PaginationPrevious,
} from "@zenoxai/ui/components/pagination"
<Pagination>
  <PaginationContent>
    <PaginationItem>
      <PaginationPrevious href="?page=1" />
    </PaginationItem>
    <PaginationItem>
      <PaginationLink href="?page=2" isActive>
        2
      </PaginationLink>
    </PaginationItem>
    <PaginationItem>
      <PaginationNext href="?page=3" />
    </PaginationItem>
  </PaginationContent>
</Pagination>

Use pagination under a list or table when the page number belongs in the URL, so the user can share or bookmark a page. Every part is a link: give each one an href. Mark the current page with isActive: it takes the selection colors (a primary-soft fill and text with a primary border), like the current entry of a Sidebar.

Show the first page, the last page and the neighbors of the current one, and replace the gaps with PaginationEllipsis. For feeds where the position does not matter, load more rows on demand instead of paginating.

Examples

Previous and next only

For long result sets where jumping to a page number is not useful, keep only the arrows and state the range next to them.

Showing 26 to 50 of 312 alerts
<div className="flex w-full items-center justify-between gap-4">
  <span className="text-sm text-text-secondary">
    Showing 26 to 50 of 312 alerts
  </span>
  <Pagination className="mx-0 w-auto">
    <PaginationContent>
      <PaginationItem>
        <PaginationPrevious href="#" />
      </PaginationItem>
      <PaginationItem>
        <PaginationNext href="#" />
      </PaginationItem>
    </PaginationContent>
  </Pagination>
</div>

Controlled

When the page lives in component state, handle onClick, call event.preventDefault() and update the state. On the first and last page, set aria-disabled on the arrow and style it: the links have no disabled prop.

function PaginationControlled() {
  const pageCount = 5
  const [page, setPage] = React.useState(1)

  return (
    <Pagination>
      <PaginationContent>
        <PaginationItem>
          <PaginationPrevious
            href="#"
            aria-disabled={page === 1}
            className="aria-disabled:pointer-events-none aria-disabled:opacity-50"
            onClick={(event) => {
              event.preventDefault()
              setPage((current) => Math.max(1, current - 1))
            }}
          />
        </PaginationItem>
        {Array.from({ length: pageCount }, (_, index) => index + 1).map(
          (number) => (
            <PaginationItem key={number}>
              <PaginationLink
                href="#"
                isActive={number === page}
                onClick={(event) => {
                  event.preventDefault()
                  setPage(number)
                }}
              >
                {number}
              </PaginationLink>
            </PaginationItem>
          )
        )}
        <PaginationItem>
          <PaginationNext
            href="#"
            aria-disabled={page === pageCount}
            className="aria-disabled:pointer-events-none aria-disabled:opacity-50"
            onClick={(event) => {
              event.preventDefault()
              setPage((current) => Math.min(pageCount, current + 1))
            }}
          />
        </PaginationItem>
      </PaginationContent>
    </Pagination>
  )
}

Accessibility

  • Pagination is a <nav aria-label="pagination"> and the active link has aria-current="page".
  • PaginationPrevious and PaginationNext hide their text below the sm breakpoint and carry the English labels “Go to previous page” and “Go to next page”. When you translate text, pass a translated aria-label too. Do the same for the aria-label of Pagination.
  • PaginationEllipsis is hidden from screen readers.

API reference

Generated from the TypeScript source. className, style and children are accepted everywhere and omitted below.

Pagination

Pagination is the row of page links under a paginated list: previous, numbered pages, an ellipsis for skipped runs, and next. Compose PaginationContent with a PaginationItem around each link.

No props of its own. Accepts the props of the element it renders.

PaginationContent

No props of its own. Accepts the props of the element it renders.

PaginationEllipsis

PaginationEllipsis marks a run of skipped pages; it is hidden from screen readers.

PropTypeDefault
labelstring

Already-translated text read by screen readers in place of the dots.

More pages

PaginationItem

No props of its own. Accepts the props of the element it renders.

PaginationLink is a page link styled as a ghost button. The current page, marked with `isActive`, takes the selection colors, like the current entry of a Sidebar or a pressed Toggle.

PropTypeDefault
isActiveboolean

Marks the current page: the selection colors (primary-soft fill and text, primary border) and aria-current="page".

-
size"default" | "xs" | "sm" | "lg" | "icon" | "icon-xs" | "icon-sm" | "icon-lg" | null

Height: `xs` 24px, `sm` 28px, `default` 32px, `lg` 36px. The `icon-*` sizes are square buttons of the same heights for a single icon. The icon follows the size name: 12px at `xs`, 14px at `sm`, 16px at `default` and `lg`.

icon

PaginationNext

PaginationNext links to the next page. Its aria-label defaults to the English "Go to next page".

PropTypeDefault
isActiveboolean

Marks the current page: the selection colors (primary-soft fill and text, primary border) and aria-current="page".

-
size"default" | "xs" | "sm" | "lg" | "icon" | "icon-xs" | "icon-sm" | "icon-lg" | null

Height: `xs` 24px, `sm` 28px, `default` 32px, `lg` 36px. The `icon-*` sizes are square buttons of the same heights for a single icon. The icon follows the size name: 12px at `xs`, 14px at `sm`, 16px at `default` and `lg`.

-
textstring

Visible label beside the chevron, shown from the sm breakpoint up.

Next

PaginationPrevious

PaginationPrevious links to the previous page. Its aria-label defaults to the English "Go to previous page".

PropTypeDefault
isActiveboolean

Marks the current page: the selection colors (primary-soft fill and text, primary border) and aria-current="page".

-
size"default" | "xs" | "sm" | "lg" | "icon" | "icon-xs" | "icon-sm" | "icon-lg" | null

Height: `xs` 24px, `sm` 28px, `default` 32px, `lg` 36px. The `icon-*` sizes are square buttons of the same heights for a single icon. The icon follows the size name: 12px at `xs`, 14px at `sm`, 16px at `default` and `lg`.

-
textstring

Visible label beside the chevron, shown from the sm breakpoint up.

Previous