# Pagination

Links that move between the pages of a long list.

```tsx
import { Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious } from "@zenoxai/ui/components/pagination"

function PaginationDemo() {
  return (
    <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

```tsx
import {
  Pagination,
  PaginationContent,
  PaginationItem,
  PaginationLink,
  PaginationNext,
  PaginationPrevious,
} from "@zenoxai/ui/components/pagination"
```

```tsx
<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](/components/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.

```tsx
import { Pagination, PaginationContent, PaginationItem, PaginationNext, PaginationPrevious } from "@zenoxai/ui/components/pagination"

function PaginationPreviousNext() {
  return (
    <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.

```tsx
import * as React from "react"
import { Pagination, PaginationContent, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious } from "@zenoxai/ui/components/pagination"

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

`import { ... } from "@zenoxai/ui/components/pagination"`

### Pagination

No props of its own.

### PaginationContent

No props of its own.

### PaginationEllipsis

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| label | `string` | `More pages` | Already-translated text read by screen readers in place of the dots. |

### PaginationItem

No props of its own.

### PaginationLink

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| isActive | `boolean` |  | 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` | `icon` | 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`. |

### PaginationNext

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| isActive | `boolean` |  | 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`. |
| text | `string` | `Next` | Visible label beside the chevron, shown from the sm breakpoint up. |

### PaginationPrevious

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| isActive | `boolean` |  | 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`. |
| text | `string` | `Previous` | Visible label beside the chevron, shown from the sm breakpoint up. |

