---
id: "packages/active-ui/pagination"
title: "Pagination"
description: "Links to the pages of a long list, with previous and next and a gap for skipped pages."
url: "/packages/active-ui/pagination"
locale: "en"
revision: "b417384b71f31fdb"
---

# Pagination

Pagination splits a long list into pages and links to them. Each page is a link with an `href`,
so it can be opened in a new tab, shared and bookmarked. It only renders the links: which pages to
show and what a page contains are your application's.

**Example: Twelve pages around the current one**

Client: Runs entirely in your browser. Source: `examples/ui/pagination.tsx`.

```tsx
// apps/docs/examples/ui/pagination.tsx
'use client';

import {useState, type MouseEvent} from 'react';

import {
  Pagination,
  PaginationContent,
  PaginationEllipsis,
  PaginationItem,
  PaginationLink,
  PaginationNext,
  PaginationPrevious,
} from '@workspace/active-ui/components/primitives/pagination';

const PAGES = 12;

/** The page numbers to show: the first, the last, and the current with its neighbours. */
function visiblePages(current: number): (number | 'gap')[] {
  const pages: (number | 'gap')[] = [];
  for (let page = 1; page <= PAGES; page++) {
    if (page === 1 || page === PAGES || Math.abs(page - current) <= 1) {
      pages.push(page);
    } else if (pages.at(-1) !== 'gap') {
      pages.push('gap');
    }
  }
  return pages;
}

/**
 * Links to the pages of a long list. Each link has a real `href`
 * (`?page=n`); this example keeps the page in state instead of navigating,
 * where an application would let the router follow the link.
 */
export default function PaginationExample() {
  const [page, setPage] = useState(5);
  const go = (target: number) => (event: MouseEvent<HTMLAnchorElement>) => {
    event.preventDefault();
    setPage(target);
  };
  return (
    <div className="flex flex-col items-center gap-3">
      <Pagination label="Pages of participants">
        <PaginationContent>
          {page > 1 ? (
            <PaginationItem>
              <PaginationPrevious
                href={`?page=${page - 1}`}
                text="Previous"
                label="Go to the previous page"
                onClick={go(page - 1)}
              />
            </PaginationItem>
          ) : null}
          {visiblePages(page).map((entry, index) =>
            entry === 'gap' ? (
              <PaginationItem key={`gap-${index}`}>
                <PaginationEllipsis />
              </PaginationItem>
            ) : (
              <PaginationItem key={entry}>
                <PaginationLink
                  href={`?page=${entry}`}
                  isActive={entry === page}
                  aria-label={`Page ${entry}`}
                  onClick={go(entry)}
                >
                  {entry}
                </PaginationLink>
              </PaginationItem>
            ),
          )}
          {page < PAGES ? (
            <PaginationItem>
              <PaginationNext
                href={`?page=${page + 1}`}
                text="Next"
                label="Go to the next page"
                onClick={go(page + 1)}
              />
            </PaginationItem>
          ) : null}
        </PaginationContent>
      </Pagination>
      <p className="text-muted-foreground text-sm" aria-live="polite">
        Page {page} of {PAGES}
      </p>
    </div>
  );
}
```

## Usage

```tsx
import {
  Pagination,
  PaginationContent,
  PaginationItem,
  PaginationLink,
  PaginationNext,
  PaginationPrevious,
} from '@workspace/active-ui/components/primitives/pagination';

<Pagination label="Pages of participants">
  <PaginationContent>
    <PaginationItem>
      <PaginationPrevious
        href="?page=4"
        text="Previous"
        label="Go to the previous page"
      />
    </PaginationItem>
    <PaginationItem>
      <PaginationLink href="?page=5" isActive>
        5
      </PaginationLink>
    </PaginationItem>
    <PaginationItem>
      <PaginationNext href="?page=6" text="Next" label="Go to the next page" />
    </PaginationItem>
  </PaginationContent>
</Pagination>;
```

## Anatomy

- `Pagination`: the `<nav>` landmark, named by `label`.
- `PaginationContent` (`<ul>`) with a `PaginationItem` (`<li>`) per link.
- `PaginationLink`: a link styled as a button. `isActive` marks the current page
  (`aria-current="page"`, outlined); `size` as on [Button](/packages/active-ui/button), `icon` by
  default.
- `PaginationPrevious` and `PaginationNext`: links with an arrow. `text` is the visible text,
  hidden below the `sm` width; `label` is the accessible name.
- `PaginationEllipsis`: the sign for skipped pages, hidden from assistive technology.

## What your application provides

- `label` of the landmark, and `text` and `label` of the previous and next links (required).
- The `href` of every link. Without one, an `<a>` is not focusable.
- Which pages to show, and leaving out the previous link on the first page and the next link on
  the last: the links have no disabled state.
- With a client-side router: let the router follow the link (`render` with your router's link, or
  `onClick`), as the example does with state.

## Accessibility and keyboard

- A `<nav>` with a list of links; `Tab` moves through them and `Enter` follows one.
- The current page is announced as such. A page number alone is a short name: give the links an
  `aria-label` such as "Page 5" when the context does not make it clear.

## API

**Pagination**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `label` | `string` | yes | The name of the navigation landmark, such as "Pages of participants". |

**PaginationContent**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |

PaginationContent has no props of its own.

**PaginationEllipsis**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |

PaginationEllipsis has no props of its own.

**PaginationItem**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |

PaginationItem has no props of its own.

**PaginationLink**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `isActive` | `boolean` | no | Whether this link is the current page: outlined, with `aria-current="page"`. |
| `size` | `"default" \| "icon" \| "icon-lg" \| "icon-sm" \| "icon-xs" \| "lg" \| "sm" \| "xs" \| null` | no | The height and padding. `xs`, `sm`, `default` and `lg` for a button with text; `icon-xs`, `icon-sm`, `icon` and `icon-lg` for a square button with only an icon, which then needs an accessible name (`aria-label`). |

**PaginationNext**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `label` | `string` | yes | The accessible name of the link, such as "Go to the next page". |
| `text` | `string` | yes | The visible text beside the arrow, hidden below the `sm` width. |
| `isActive` | `boolean` | no | Whether this link is the current page: outlined, with `aria-current="page"`. |
| `size` | `"default" \| "icon" \| "icon-lg" \| "icon-sm" \| "icon-xs" \| "lg" \| "sm" \| "xs" \| null` | no | The height and padding. `xs`, `sm`, `default` and `lg` for a button with text; `icon-xs`, `icon-sm`, `icon` and `icon-lg` for a square button with only an icon, which then needs an accessible name (`aria-label`). |

**PaginationPrevious**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `label` | `string` | yes | The accessible name of the link, such as "Go to the next page". |
| `text` | `string` | yes | The visible text beside the arrow, hidden below the `sm` width. |
| `isActive` | `boolean` | no | Whether this link is the current page: outlined, with `aria-current="page"`. |
| `size` | `"default" \| "icon" \| "icon-lg" \| "icon-sm" \| "icon-xs" \| "lg" \| "sm" \| "xs" \| null` | no | The height and padding. `xs`, `sm`, `default` and `lg` for a button with text; `icon-xs`, `icon-sm`, `icon` and `icon-lg` for a square button with only an icon, which then needs an accessible name (`aria-label`). |

> [!NOTE]
> **Based on shadcn/ui**
>
> This page adapts the Pagination documentation of shadcn/ui (MIT licence) to
> this package; see [Third-party notices](/third-party-notices).
