Propel

Pagination

Navigation for a paginated list or table with page numbers and prev/next controls.

Show code
import { Pagination } from "@makeplane/propel/components/pagination";
import * as React from "react";

export default function BasicDemo() {
  const [page, setPage] = React.useState(1);

  return <Pagination page={page} pageCount={25} onPageChange={setPage} />;
}

Installation

import { Pagination } from "@makeplane/propel/components/pagination";

Usage

import { Pagination } from "@makeplane/propel/components/pagination";
import * as React from "react";

export default function BasicDemo() {
  const [page, setPage] = React.useState(1);

  return <Pagination page={page} pageCount={25} onPageChange={setPage} />;
}

Examples

With selector and range

Pass pageSize to add the per-page selector and range to show the 1-50 of 250 label before the controls; both are optional.

Show code
import { Pagination } from "@makeplane/propel/components/pagination";
import * as React from "react";

export default function WithSelectorAndRangeDemo() {
  const [page, setPage] = React.useState(1);
  const [pageSize, setPageSize] = React.useState(50);

  return (
    <Pagination
      page={page}
      pageCount={5}
      onPageChange={setPage}
      pageSize={{ value: pageSize, options: [25, 50, 100], onValueChange: setPageSize }}
      range={{ current: "1-50", total: 250 }}
    />
  );
}

Truncation

The visible page window is derived from where the current page sits within pageCount — first and last are always anchored, and runs of two or more skipped pages collapse to an ellipsis.

Show code
import { Pagination } from "@makeplane/propel/components/pagination";

export default function TruncationDemo() {
  return (
    <div className="flex flex-col gap-4">
      <Pagination
        page={1}
        pageCount={5}
        onPageChange={() => {}}
        labels={{ root: "All pages visible" }}
      />
      <Pagination
        page={1}
        pageCount={100}
        onPageChange={() => {}}
        labels={{ root: "Near start" }}
      />
      <Pagination page={45} pageCount={100} onPageChange={() => {}} labels={{ root: "Middle" }} />
      <Pagination
        page={100}
        pageCount={100}
        onPageChange={() => {}}
        labels={{ root: "Near end" }}
      />
    </div>
  );
}

Empty state

When there’s nothing to page through (pageCount is 0 or 1), prefer not rendering Pagination at all. If pageCount is 0, the control still renders safely — no page-number button, both arrows disabled — rather than showing a stray “page 1”.

Loading

Set loading to render the current page as a spinner while navigating to it is in flight.

Show code
import { Pagination } from "@makeplane/propel/components/pagination";

export default function LoadingDemo() {
  return <Pagination page={3} pageCount={25} onPageChange={() => {}} loading />;
}

Labels

labels overrides the default English accessible names (landmark, prev/next, page buttons) and the selector’s visible text — pass any subset; the rest keep their defaults.

Show code
import { Pagination } from "@makeplane/propel/components/pagination";
import * as React from "react";

export default function LabelsDemo() {
  const [page, setPage] = React.useState(1);
  const [pageSize, setPageSize] = React.useState(50);

  return (
    <Pagination
      page={page}
      pageCount={5}
      onPageChange={setPage}
      pageSize={{ value: pageSize, options: [25, 50, 100], onValueChange: setPageSize }}
      labels={{
        root: "Work item pages",
        previous: "Previous page",
        next: "Next page",
        page: (pageNumber) => `Page ${pageNumber}`,
        perPage: "rows per page",
      }}
    />
  );
}

API Reference

Pagination

Page navigation for a paginated list or table. Renders a `<nav>` landmark holding an optional per-page selector and range label plus an ordered list of page controls: a previous button, first/last anchors with ellipses around a window of pages near the current one, and a next button. The current page is marked `aria-current="page"`; the prev/next ends disable at the bounds and their arrows mirror under RTL.

PropTypeDefaultDescription
page(required)numberThe current page, 1-based.
pageCount(required)numberTotal number of pages.
onPageChange(required)(page: number) => voidCalled with the target page (1-based) when a control is activated.
loadingbooleanfalseThe current page is rendered as a loading spinner instead of its number — for when navigating to that page is in flight.
pageSize{ value: number; options: number[]; onValueChange: (pageSize: number) => void; }Optional per-page size selector (Figma `50 v per page`). Provide `value`, the `options`, and `onValueChange`; omit to hide the selector entirely.
range{ current: ReactNode; total: ReactNode; }Optional range label shown before the controls (Figma `1-50 of 250`). Provide the already-formatted `current` range and `total`.
labelsPartial<PaginationLabels>Override the default English accessible names / visible selector text.