# Pager

```tsx
import { Pager } from '@tangible/ui';
```

Interactive docs and examples: [Pager in Storybook](https://storybook-tangible-ui.pages.dev/?path=/docs/primitives-pager--docs).

## Pager props

| Prop | Type | Default | Description |
| - | - | - | - |
| `currentPage` | `number` | | Current page (1-indexed) |
| `onPageChange` | `(page: number) => void` | | Called when user selects a different page |
| `totalPages` | `number` | | Total number of pages |
| `className?` | `string` | | Additional class name |
| `hidden?` | `boolean` | `false` | Hide the entire pager (useful when only 1 page) |
| `labels?` | `PagerLabels` | | Overridable label strings for i18n. All keys are optional — defaults are English. |
| `maxNumbers?` | `number` | `7` | Max page number buttons in smart mode |
| `mode?` | `"ends" \| "full" \| "simple" \| "smart"` | `'smart'` | Pagination mode: 'simple' (prev/next only), 'ends', 'full', 'smart' |
| `navStyle?` | `"icon" \| "text"` | `'text'` | Style for prev/next: 'text' shows labels, 'icon' shows chevrons |
| `onPageSizeChange?` | `(size: number) => void` | | Called when user changes page size |
| `pageSize?` | `number` | | Current page size (for page size selector) |
| `pageSizeOptions?` | `number[]` | | Available page size options |
| `showFirstLast?` | `boolean` | `false` | Add dedicated first-page / last-page buttons outside prev/next. Most valuable in `simple` mode and at high page counts — in `smart`, `ends` and `full` the numbered group already carries a "1" and a last page, so these mainly save the user having to find them. Opt-in for that reason: four nav buttons is a lot of chrome to add by default. In `flush` they join the connected group at the ends, which means prev and next stop being the end segments — the border-collapse rules key off actual position rather than kind, so this needs no special casing. |
| `showInfo?` | `boolean` | `true` | Show the "Page X of Y" region. The page size selector has its own condition (`pageSizeOptions` + `onPageSizeChange`), so this governs the region, not the select. Defaults to `true` in every mode — including `simple`, where the status line is arguably redundant. Auto-hiding there is what most consumers probably want, but it would silently change layout for existing `simple` users, so it stays opt-out. |
| `showJump?` | `boolean` | `false` | Add a "Go to page" field. For hundreds of pages, where the numbered buttons and even first/last are still slow. Commits on Enter or via its own submit button — deliberately not on blur, which would move the page out from under a user who tabbed away mid-edit. Out-of-range entries clamp to the nearest valid page rather than erroring, and the existing `role="status"` region announces where the jump actually landed. It renders outside the `<nav>` landmark, which is the connected button group itself — so someone arriving by landmark finds prev/next and the numbers, not this field. It follows immediately in tab order. Moving it inside would mean wrapping the group rather than being it, and the field would then have to sit beside the buttons instead of at the trailing edge. |
| `size?` | `"lg" \| "md" \| "sm"` | `'sm'` | Control size — joins the shared control-height ladder (sm/md/lg) that Button, IconButton, and the form controls use. |
| `variant?` | `"detached" \| "flush"` | `'flush'` | Whether nav segments are joined into one group or spaced apart. |

## Other exports

- `defaultPagerLabels`: `Required<PagerLabels>`

## Examples

- [Simple Mode](https://storybook-tangible-ui.pages.dev/?path=/story/primitives-pager--simple-mode): Simple mode shows only Prev/Next buttons without page numbers.

- [Full Mode](https://storybook-tangible-ui.pages.dev/?path=/story/primitives-pager--full-mode): Full mode shows all page numbers. Best for small page counts.

- [With Page Size Selector](https://storybook-tangible-ui.pages.dev/?path=/story/primitives-pager--with-page-size-selector): With page size selector for adjustable items per page.

- [Many Pages](https://storybook-tangible-ui.pages.dev/?path=/story/primitives-pager--many-pages): Smart mode handles large page counts with ellipsis.

- [Few Pages](https://storybook-tangible-ui.pages.dev/?path=/story/primitives-pager--few-pages): With only a few pages, all numbers are shown.

- [Single Page](https://storybook-tangible-ui.pages.dev/?path=/story/primitives-pager--single-page): Single page with both nav buttons disabled.

- [Ends Mode](https://storybook-tangible-ui.pages.dev/?path=/story/primitives-pager--ends-mode): Ends mode always keeps the first and last page visible, with ellipsis collapsing the middle. Compare with smart mode, which windows around the current page.

- [Localised Labels](https://storybook-tangible-ui.pages.dev/?path=/story/primitives-pager--localised-labels): Every announced string routes through `labels` — nav landmark name, button labels, and the `role="status"` live region that announces page changes. **WCAG 2.5.3 Label in Name:** the visible button text (`previousText`/`nextText`) must be contained in the accessible name (`previous`/`next`), or voice-control users can't activate the buttons by saying what they see — keep that containment when translating.

- [Detached (text nav)](https://storybook-tangible-ui.pages.dev/?path=/story/primitives-pager--detached-variant): `variant="detached"` spaces the segments apart instead of joining them into a
  connected group — the pill-style pagination common on front-ends, as opposed
  to the admin-table look `flush` gives.

  Prev/next carry no border or fill in this variant: they sit outside the group
  as ghost affordances rather than outline segments completing it.

- [Detached (icon nav)](https://storybook-tangible-ui.pages.dev/?path=/story/primitives-pager--detached-icon-nav): The same variant with `navStyle="icon"` — prev/next reduce to bare chevrons.

- [Variants compared](https://storybook-tangible-ui.pages.dev/?path=/story/primitives-pager--variant-comparison): Both variants together, for comparison. The gap is the only structural
  difference in the number group — `--tui-pager-nav-gap` drives it, and the
  flush variant zeroes it.

  Note the distinct `labels.navigation` on each. Every Pager renders a `<nav>`
  landmark named "Pagination" by default, so two on one page are
  indistinguishable to a screen reader moving by landmark (axe flags it as
  `landmark-unique`). Any page with more than one Pager — the common case being
  one above and one below a long table — has to differentiate them.

- [Simple mode, no info](https://storybook-tangible-ui.pages.dev/?path=/story/primitives-pager--simple-mode-without-info): `showInfo={false}` drops the "Page X of Y" region. Most useful in `simple`
  mode, where the status line repeats context the prev/next pair already
  implies — which is why it always rendered there and could not be removed.

  It governs the region, not the page size selector: that has its own condition
  (`pageSizeOptions` + `onPageSizeChange`).

- [First / last (flush)](https://storybook-tangible-ui.pages.dev/?path=/story/primitives-pager--first-last-buttons): `showFirstLast` adds dedicated first/last buttons outside prev/next, so the
  order reads « ‹ 1 2 3 › ». They are nav affordances rather than page numbers,
  so they take the same treatment as prev/next in both variants.

  In `flush` they join the connected group at the ends — which means prev and
  next are no longer the end segments. The border-collapse rules key off actual
  position rather than which kind of button it is, so the outer radius lands on
  the new ends without any special casing.

- [First / last (icon nav)](https://storybook-tangible-ui.pages.dev/?path=/story/primitives-pager--first-last-icon-nav): With `navStyle="icon"`, first/last are double chevrons — `system/chevron-left-double`
  and `system/chevron-right-double`, added for this affordance. The doubling is what
  distinguishes them from prev/next at a glance.

- [First / last (simple mode)](https://storybook-tangible-ui.pages.dev/?path=/story/primitives-pager--simple-mode-with-first-last): Where first/last earn their keep most: `simple` mode has no numbered group at
  all, so without them the only way back to page 1 is to click Prev repeatedly.

- [First / last (detached)](https://storybook-tangible-ui.pages.dev/?path=/story/primitives-pager--first-last-detached): Detached, for the ghost treatment on all four nav buttons at once.

- [Go to page](https://storybook-tangible-ui.pages.dev/?path=/story/primitives-pager--with-jump): `showJump` adds a go-to-page field for page counts where even first/last are
  slow. It commits on **Enter** or via its own submit button — not on blur,
  which would move the page out from under someone who tabbed away mid-edit.

  Out-of-range entries **clamp** rather than error: typing `999` here goes to
  page 40, and the Pager's existing `role="status"` region announces where the
  jump actually landed, so the correction is audible rather than silent. The
  field clears on commit — "Page X of Y" is what displays state, so the input
  never sits there disagreeing with a page reached some other way.

- [Everything on](https://storybook-tangible-ui.pages.dev/?path=/story/primitives-pager--kitchen-sink): Everything at once: first/last, the numbered group, the info region, a page
  size selector and the jump field — the densest the Pager gets, and a check
  that the row still lays out sensibly when it wraps.

- [Go to page, no info](https://storybook-tangible-ui.pages.dev/?path=/story/primitives-pager--jump-without-info): The jump field with no info region — `showInfo={false}` removes "Page X of Y"
  and the page size selector, and the jump still sits at the trailing end. The
  nav owns the auto margin that pushes it there, so it does not matter which of
  the trailing regions render.

- [Responsive (container query)](https://storybook-tangible-ui.pages.dev/?path=/story/primitives-pager--responsive): The Pager is a **container query** container (`container-name: tuiPager`), so
  it responds to the width it is given rather than to the viewport — the same
  approach Card and Notice use.

  Two things happen as the space narrows:
  1. **The nav wraps.** Unconditionally, not below a breakpoint: the nav's width
     depends on `mode`, `maxNumbers`, `showFirstLast` and `size`, so no single
     threshold can promise it fits — `mode="full"` at 30 pages is wider than any
     sane breakpoint. Wrapping costs nothing while it fits, and the alternative
     is the nav pushing the page into horizontal scroll (WCAG 2.1 SC 1.4.10).
  2. **Below `32rem`, `flush` adopts the detached presentation.** A connected
     group cannot wrap and still look like one: the collapse rules key off
     `:first-child`/`:last-child`, and CSS cannot identify the first item of a
     *wrapped row*, so a second row would open with a borderless, square-cornered
     box. Pills wrap correctly by construction.
  `32rem` is a design line rather than a derived number, so a narrow pager
  degrades slightly before it strictly has to. The rules sit at the same
  specificity as the flush block, so a consumer can override the call.

Other stories: Default, Icon Navigation, Sizes ([interactive page](https://storybook-tangible-ui.pages.dev/?path=/docs/primitives-pager--docs)).

## Component API tokens

Read via fallback, never declared. Set one on an ancestor to restyle every instance under it.

- `--tui-pager-gap`
- `--tui-pager-nav-gap`
- `--tui-pager-item-border`
- `--tui-pager-item-radius`
- `--tui-pager-jump-width`

---

Interactive version: https://storybook-tangible-ui.pages.dev/?path=/docs/primitives-pager--docs
