> ## Documentation Index
> Fetch the complete documentation index at: https://anvil.servicetitan.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Pagination - Web

> The Pagination helps people move through large result sets one page at a time, stay oriented, and jump directly to the page they need

> [!NOTE]
>
> For development, import `Pagination` from
> `@servicetitan/anvil2`. For design, find it in the
> [Anvil2 file](https://www.figma.com/design/xaNQsvRKczoxu7pj0M6QT8/Anvil2-v3?node-id=158-7744).

## Anatomy

![Pagination Anatomy Pn](https://mintcdn.com/servicetitan/Q4o0kHYyt4JowUYo/images/pagination-anatomy.png?fit=max\&auto=format\&n=Q4o0kHYyt4JowUYo\&q=85\&s=eedc62485fe74a29543396970b82d270)

1. **Previous Button** — moves to the prior page; disabled on the first page.
2. **Page Button** — numeric buttons for direct jumps.
3. **Truncated Page Dropdown Button** — ellipsis “⋯” that opens a jump menu for hidden ranges.
4. **Next Button** — moves to the next page; disabled on the last page (when total is known).
5. **Items per page select** — sets page size (e.g., 10/25/50/100).
6. **Counter** — displays the current range and total (e.g., “1 – 10 of 100 items”).

***

## Options

### Total count availability

When the total item count is unknown, the component switches to Unknown total mode and does not show Page Buttons or the Counter. Navigation is via Previous/Next, and users can still adjust Items per page.

#### Known total

![Pagination Known Total Pn](https://mintcdn.com/servicetitan/Q4o0kHYyt4JowUYo/images/pagination-known-total.png?fit=max\&auto=format\&n=Q4o0kHYyt4JowUYo\&q=85\&s=bf3cc4ea245e10c41696e82351bb48c7)

#### Unknown total

![Pagination Unknown Total Pn](https://mintcdn.com/servicetitan/Q4o0kHYyt4JowUYo/images/pagination-unknown-total.png?fit=max\&auto=format\&n=Q4o0kHYyt4JowUYo\&q=85\&s=bff2d026e667ca693246929d808e4046)

### Truncation strategy

* **Max visible Page Buttons:** 7 total.
* **First/last pinned:** pages `1` and `last` are always visible.
* **Window:** show **current** with up to two neighbors on each side; replace hidden ranges with a **“⋯” dropdown**.
* **Small sets:** if total pages **≤ 7**, show all pages with no truncation.
* **Examples:**

  * Start (page 1): `1  2  3  4  5  ⋯  100`
  * End (page 100): `1  ⋯  96  97  98  99  100`
  * Middle (page 55): `1  ⋯  54  55  56  ⋯  100`

  ![Pagination Truncation Pn](https://mintcdn.com/servicetitan/Q4o0kHYyt4JowUYo/images/pagination-truncation.png?fit=max\&auto=format\&n=Q4o0kHYyt4JowUYo\&q=85\&s=c9132c84512a93a80b8780a884d81378)

### Counter format

* **Known total:** “**Start – End of Total items**” (e.g., “1 – 10 of 100 items”).
* **Unknown total:** “**Start – End items**” (e.g., “1 – 10 items”).

***

## Usage Guidelines

Use the **Pagination** when dividing large result sets into predictable, numbered segments to support direct navigation and orientation.

| Standard | Requirement |
| - | - |
| DoJumping to a specific page | Use Pagination when users need to go directly to a page, not only move to the next or previous one. |
| DoShowing the total count | Use Pagination when the total number of items helps users orient themselves, such as "1 – 10 of 100 items". |
| DoPredictable controls | Use Pagination when navigation controls need to stay in a fixed, predictable place. |
| DoPlace below results | Place Pagination directly below the results it controls. |
| DoConsistent page-size presets | Use the same Items per page options, such as 10, 25, 50, and 100, across products. |
| Don'tContinuous content | Use infinite scroll instead when content is a continuous feed with no need to return to a specific page. |
| Don'tVery small result sets | Show all results on one page when there are only a few. |
| Don'tTask-progress steps | Use a [Stepper](https://anvil.servicetitan.com/docs/components/stepper/web.md) to show progress through the steps of a task. |

### When to Use

Use the Pagination when:

* **Large result sets** – Results are too many to show at once and divide into predictable, numbered pages.
* **Precise jumps** – Users need to jump directly to a specific page.
* **Meaningful totals** – The total count helps users understand how many results exist and where they are within them.
* **Stable placement** – Results need to keep a predictable position across pages.
* **Unknown totals** – The total item count is unavailable. The Pagination switches to Unknown total mode and navigates with the Previous and Next Buttons.

### When not to use

Avoid using the Pagination for:

* **Continuous content** – Content reads as one continuous stream; use infinite scroll instead.
* **Small result sets** – Results are very small; show all results instead.
* **Task progress** – Steps represent progress through a task; use a [Stepper](https://anvil.servicetitan.com/docs/components/stepper/web.md) instead.
* **Data Table rows** – The Data Table pages its own rows; use the [Data Table's built-in pagination](https://anvil.servicetitan.com/docs/components/data-table/web#pagination) instead of a separate Pagination.

***

## Content

Content within the **Pagination** should keep the counter clear and context-aware.

* **Counter noun (“items”) is contextual:** replace items with the domain noun (e.g., orders, records, messages) to reflect what’s being paged.
  * Examples: “1 – 10 of 100 orders”, “1 – 25 of 325 records”, “1 – 1 of 1 message”.
* **Singular/plural:** use message (singular) when End and Total are **1**; otherwise use the plural form.
* **Unknown totals:** the counter stays hidden. Do not show a range noun when the total is unknown.
* **Formatting:** use an en dash with spaces (`1 – 10`) and sentence case for the noun.

## Code

**Implementation**

> [!NOTE]
>
> **Looking for design and usage docs?**
>
> The Design Spec and Usage Guidelines documentation for the Pagination component will be available soon. Reach out to our team in the [#ask-designsystem](https://servicetitan.enterprise.slack.com/archives/CBSRGHTRS) channel if you have questions or need assistance!

pagination-playground

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/pagination-playground?file=src/App.tsx)

```tsx lines expandable theme={null}
import { Pagination, Flex } from "@servicetitan/anvil2";
import { useState } from "react";

function App() {
  const totalItemCount = 20;
  const [page, setPage] = useState(1);
  const [itemsPerPage, setItemsPerPage] = useState(4);
  const itemsPerPageOptions = [4, 6, 10];

  return (
    <Flex direction="column" style={{ padding: 24 }} gap="2">
      <Pagination
        page={page}
        itemsPerPage={itemsPerPage}
        itemsPerPageOptions={itemsPerPageOptions}
        onItemsPerPageChange={(itemsPerPage) => setItemsPerPage(itemsPerPage)}
        totalItemCount={totalItemCount}
        onPageChange={(page) => setPage(page)}
        showCount={true}
      />
    </Flex>
  );
}

export default App;
```

## Common Examples

### Using with Card grid

pagination-card

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/pagination-card?file=src/App.tsx)

```tsx lines expandable theme={null}
import { Pagination, Flex, Grid, Card } from "@servicetitan/anvil2";
import { useState } from "react";

function App() {
  const totalItemCount = 100;
  const items = [...Array(totalItemCount).keys()];
  const [page, setPage] = useState(1);
  const [itemsPerPage, setItemsPerPage] = useState(4);
  const itemsPerPageOptions = [4, 8, 12];
  const startIndex = (page - 1) * itemsPerPage;
  const endIndex = page * itemsPerPage;
  const pageItems = items.slice(startIndex, endIndex);

  return (
    <Flex direction="column" style={{ padding: 24 }} gap="2">
      <Grid templateColumns="repeat(2, 1fr)" gap="2">
        {pageItems.map((item) => (
          <Card key={item}>{item + 1}</Card>
        ))}
      </Grid>
      <Pagination
        page={page}
        itemsPerPage={itemsPerPage}
        itemsPerPageOptions={itemsPerPageOptions}
        onItemsPerPageChange={(itemsPerPage) => setItemsPerPage(itemsPerPage)}
        totalItemCount={totalItemCount}
        onPageChange={(page) => setPage(page)}
        showCount={true}
      />
    </Flex>
  );
}

export default App;
```

**Testing**

## Testing

Pagination's interactive controls carry a stable `data-anv-part` attribute so end-to-end tests can target them directly. The library owns the part names; you supply the instance identifier.

Attach your own `data-*` attribute (such as `data-cy` or `data-testid`) to `Pagination`. It is forwarded to the root `<nav>`:

```tsx theme={null}
<Pagination data-cy="orders-pagination" page={1} itemsPerPage={10} totalItemCount={100} {...rest} />
```

```ts theme={null}
cy.get('[data-cy="orders-pagination"] [data-anv-part="next-page-button"]').click();
cy.get('[data-cy="orders-pagination"] [data-anv-part="page-button"][data-page="3"]').click();
```

| `data-anv-part` | Element |
| - | - |
| `prev-page-button` | Previous page control |
| `next-page-button` | Next page control |
| `page-button` | Numbered page control (`data-page`) |
| `overflow-trigger` | Ellipsis that opens the overflow page menu |
| `items-per-page-trigger` | Control that opens the items-per-page menu |
| `option` | Menu option in overflow or items-per-page menus |

Narrow repeated parts with `data-page` (page buttons / overflow options) or `data-items-per-page` (items-per-page options).

> [!NOTE]
>
> Overflow and items-per-page menus render in the browser's top layer. Descendant queries scoped to your wrapper attribute will not match `option` parts. Target them by part attribute (and discriminator) after opening the matching trigger. Only one menu is open at a time.

**Pagination Props**

```tsx theme={null}
import { Pagination } from "@servicetitan/anvil2";

function ExampleComponent() {
return (

<Pagination
  page={1}
  itemsPerPage={10}
  totalItemCount={100}
  itemsPerPageOptions={[10, 20, 50]}
  showCount={false}
  onPageChange={(page) => console.log(page)}
  onItemsPerPageChange={(itemsPerPage) => console.log(itemsPerPage)}
/>
); }

```

## `Pagination` Props

In addition to the props listed below, the `Pagination` component can accept any valid HTML `nav` props.

**itemsPerPage** `number` *required*

Number of items displayed per page

**page** `number` *required*

The current page number

**itemRangeFormatter** `(firstItem: number, lastItem: number, totalItemCount: number) =\> string`

Full counter string. Called with the first visible item (1-based), the last visible item, and the total. When `totalItemCount` is `0`, all three arguments are `0`. Not called when `totalItemCount` is omitted. When this prop is omitted, the counter reads "1 - 10 of 100 items" (or "0 items"). The same string follows the English `Page {page} of {totalPages}` prefix in the live region.

**itemsPerPageLabel** `string` (default: "Rows per page")

Custom label displayed before the items-per-page selector

**itemsPerPageOptions** `number[]`

Available options for items per page selection

**onItemsPerPageChange** `(itemsPerPage: number) =\> void`

Callback when items per page changes

**onPageChange** `(page: number) =\> void`

Callback when page changes

**showCount** `boolean`

Whether to display the item count information

**totalItemCount** `number`

Total number of items across all pages


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.