Skip to content

Pagination

Composable pagination primitives for navigating paged content without routing or data logic.

The Pagination component provides composable UI primitives for navigating paged content.

It is intentionally dumb:

  • no router integration
  • no fetching logic
  • no URL sync
  • no cursor logic

Pagination is fully controlled by the page or data layer. The UI only renders navigation.


Import

import {
  Pagination,
  PaginationContent,
  PaginationItem,
  PaginationLink,
  PaginationPrevious,
  PaginationNext,
  PaginationEllipsis,
  buildPaginationRange,
} from "@pycolors/ui";

Usage

Pagination is composed of small building blocks:

  • Pagination → wrapper (nav)
  • PaginationContent → list container (ul)
  • PaginationItem → item wrapper (li)
  • PaginationLink → page button (active state)
  • PaginationPrevious / PaginationNext → navigation
  • PaginationEllipsis → compact overflow indicator

Control page state from your page or component.


Basic pagination

Page 1 / 12

function PaginationBasicExample() {
  const [page, setPage] = React.useState(1);
  const totalPages = 12;

  const tokens = buildPaginationRange({ page, totalPages });

  return (
    <Pagination>
      <PaginationContent>
        <PaginationItem>
          <PaginationPrevious
            disabled={page === 1}
            onClick={() => setPage((p) => Math.max(1, p - 1))}
          />
        </PaginationItem>

        {tokens.map((t) =>
          t.type === "ellipsis" ? (
            <PaginationItem key={t.key}>
              <PaginationEllipsis />
            </PaginationItem>
          ) : (
            <PaginationItem key={t.value}>
              <PaginationLink
                isActive={t.isActive}
                onClick={() => setPage(t.value)}
              >
                {t.value}
              </PaginationLink>
            </PaginationItem>
          )
        )}

        <PaginationItem>
          <PaginationNext
            disabled={page === totalPages}
            onClick={() => setPage((p) => Math.min(totalPages, p + 1))}
          />
        </PaginationItem>
      </PaginationContent>
    </Pagination>
  );
}

Table + Pagination (mock data)

This example demonstrates a realistic SaaS pattern: a Table with a local Pagination control.

No backend logic is included — replace the mock slice with your real data layer later.

Mock data — swap with Prisma later.
NameStatusUpdatedActions
Project 1Active2026-01-21
Project 2Active2026-01-21
Project 3Paused2026-01-21
Project 4Active2026-01-21
Project 5Archived2026-01-21

Showing 15 of 23

type Project = {
  id: string;
  name: string;
  status: "active" | "paused" | "archived";
  updatedAt: string;
};

// Example idea (pseudo):
// - keep `page` in state
// - compute a slice from your dataset
// - pass `page` + `totalPages` into Pagination
//
// const pageSize = 10;
// const totalPages = Math.ceil(projects.length / pageSize);
// const pageProjects = projects.slice((page - 1) * pageSize, page * pageSize);

API

Components

ComponentDescription
PaginationWrapper nav element (aria-label="Pagination")
PaginationContentList container (ul)
PaginationItemItem wrapper (li)
PaginationLinkPage button with active state
PaginationPreviousPrevious button (responsive label)
PaginationNextNext button (responsive label)
PaginationEllipsisVisual overflow indicator
PropTypeDefaultDescription
isActivebooleanfalseMarks the current page (aria-current="page")
sizestring"icon-sm"Maps to Button sizes
classNamestringAdditional Tailwind classes

Accessibility

  • Uses semantic nav with aria-label="Pagination".
  • Active page sets aria-current="page".
  • Buttons remain fully keyboard accessible.
  • Ellipsis is non-interactive and screen-reader labeled.

Design guidelines

  • Keep pagination UI-only, controlled by the page.
  • Prefer small, consistent page sizes for tables.
  • Use ellipsis for large page counts.
  • Sync with URL and data layers in guides or patterns, not inside the component.

When not to use Pagination

Do not use Pagination for:

  • infinite scroll experiences
  • cursor-based APIs without a page concept
  • global navigation logic

Those belong in patterns or product-level implementations.