NimUI
ComponentsNavigation

Pagination

Composable pagination building blocks for navigating through pages of content

The Pagination component provides composable building blocks for page navigation. It renders semantic <nav> with aria-label="pagination" and uses anchor elements that work with any routing library. Pair with the usePagination helper to generate consistent page ranges with ellipsis.

Import

import {
  Pagination,
  PaginationContent,
  PaginationItem,
  PaginationLink,
  PaginationPrevious,
  PaginationNext,
  PaginationEllipsis,
  usePagination,
} from '@nim-ui/components';

Basic Usage

Basic Pagination
Code
<Pagination>
  <PaginationContent>
    <PaginationItem>
      <PaginationPrevious href="?page=1" />
    </PaginationItem>
    <PaginationItem>
      <PaginationLink href="?page=1">1</PaginationLink>
    </PaginationItem>
    <PaginationItem>
      <PaginationLink href="?page=2" isActive>2</PaginationLink>
    </PaginationItem>
    <PaginationItem>
      <PaginationLink href="?page=3">3</PaginationLink>
    </PaginationItem>
    <PaginationItem>
      <PaginationEllipsis />
    </PaginationItem>
    <PaginationItem>
      <PaginationNext href="?page=3" />
    </PaginationItem>
  </PaginationContent>
</Pagination>

With usePagination Helper

The usePagination helper computes a page range with ellipsis markers based on the current page, total pages, and a sibling count:

import { usePagination } from '@nim-ui/components';

function MyPagination({ currentPage, totalPages, onPageChange }) {
  const pages = usePagination({
    totalPages,
    currentPage,
    siblingCount: 1,
  });

  return (
    <Pagination>
      <PaginationContent>
        <PaginationItem>
          <PaginationPrevious
            onClick={() => onPageChange(Math.max(1, currentPage - 1))}
          />
        </PaginationItem>
        {pages.map((page, i) => (
          <PaginationItem key={i}>
            {page === 'ellipsis' ? (
              <PaginationEllipsis />
            ) : (
              <PaginationLink
                isActive={page === currentPage}
                onClick={() => onPageChange(page)}
              >
                {page}
              </PaginationLink>
            )}
          </PaginationItem>
        ))}
        <PaginationItem>
          <PaginationNext
            onClick={() =>
              onPageChange(Math.min(totalPages, currentPage + 1))
            }
          />
        </PaginationItem>
      </PaginationContent>
    </Pagination>
  );
}

Example output

usePagination({ totalPages: 20, currentPage: 10, siblingCount: 1 })
// => [1, 'ellipsis', 9, 10, 11, 'ellipsis', 20]

usePagination({ totalPages: 5, currentPage: 3 })
// => [1, 2, 3, 4, 5]

usePagination({ totalPages: 20, currentPage: 2 })
// => [1, 2, 3, 'ellipsis', 20]

Props

NameTypeDefaultDescription
isActivebooleanfalseMark this page as the current/active page (sets aria-current="page")
size'sm' | 'md' | 'lg''md'Size variant for the link button
hrefstring-URL for the pagination link
classNamestring-Additional CSS classes

usePagination

NameTypeDefaultDescription
totalPages*number-Total number of pages
currentPage*number-Current active page (1-indexed)
siblingCountnumber1Number of sibling pages to show on each side of the current page
showEdgesbooleantrueAlways show the first and last page

Accessibility

  • Root is a <nav> landmark with aria-label="pagination"
  • Active page has aria-current="page"
  • Previous/Next buttons have descriptive aria-labels
  • Ellipsis is aria-hidden="true" with a screen-reader-only "More pages" text
  • Focus-visible rings use the primary token for consistent keyboard feedback

On this page