---
title: Pagination
description: A control for moving between pages of data.
category: Navigation
status: beta
since: 0.3.4
tags: [pagination, pages, navigation]
syncStatus: reviewed
verified: true
---

<script setup lang="ts">
import { ref } from 'vue'
const page = ref(1)
</script>

## Usage

### Basic
`total` and `pageSize` set the page count; `show-info` adds page info and `show-first-last` adds first and last buttons.
:::TuffDemoWrapper{demo="PaginationPaginationDemo" code-lang="vue"}
---
code: |
  <template>
    <TxPagination
      v-model:current-page="page"
      :total="120"
      show-info
      show-first-last
    />
  </template>
---
:::

### Page Size
`page-sizes` adds a size selector after the page buttons. Changing the size leaves the page alone; the host decides whether to return to page 1.
:::TuffDemoWrapper{demo="PaginationPageSizeDemo" code-lang="vue"}
---
code: |
  <template>
    <TxPagination
      v-model:current-page="page"
      v-model:page-size="pageSize"
      :page-sizes="[10, 20, 50]"
      page-size-label="Items per page"
      :total="230"
      show-info
      @page-size-change="page = 1"
    >
      <template #info="{ currentPage, totalPages }">
        230 items · page {{ currentPage }} of {{ totalPages }}
      </template>
    </TxPagination>
  </template>
---
:::

### Data Operations
Place pagination right below the table, bound to the same reactive state as filtering and selection.
::TuffDemoWrapper{demo="ComponentsDataOperationsDemo" code-lang="vue" description="With DataTable and Skeleton"}
---
code: |
  <template>
    <TxPagination
      v-model:current-page="page"
      :total="rows.length"
      :page-size="4"
      show-info
      show-first-last
    />
  </template>
---
::

### Best Practices

- Prefer `total` with `pageSize`; use `totalPages` only when the backend returns a page count alone.
- Keep `currentPage` one-based and initialize it to `1`.
- Reset `currentPage` to `1` when filters, search terms, or the page size change.
- Use `pageSizes` instead of your own size select: it is named by a visible label and shares the pagination's row.
- Use the `info` slot for localized range copy, such as "Viewing 21–40 of 120 items".

## API Reference

### Props
::TuffPropsTable
---
rows:
  - name: currentPage
    type: number
    default: '1'
    description: Current page, counted from 1
  - name: pageSize
    type: number
    default: '10'
    description: Items per page; pair with v-model:page-size when the reader can change it
  - name: pageSizes
    type: 'number[]'
    default: '[]'
    description: 'Size choices that render a size selector; invalid sizes are dropped, and a missing pageSize joins them'
  - name: pageSizeLabel
    type: string
    default: "'Items per page'"
    description: Visible label before the size selector; also names the selector
  - name: total
    type: number
    default: '-'
    description: Total item count; when omitted or 0, totalPages is used instead
  - name: totalPages
    type: number
    default: '-'
    description: Explicit page count, used when total is not provided
  - name: prevIcon
    type: string
    default: "''"
    description: Previous-page icon class rendered by TxIcon; empty uses the built-in chevron
  - name: nextIcon
    type: string
    default: "''"
    description: Next-page icon class rendered by TxIcon; empty uses the built-in chevron
  - name: showInfo
    type: boolean
    default: 'false'
    description: Shows page and total info
  - name: showFirstLast
    type: boolean
    default: 'false'
    description: Shows first and last buttons
  - name: ariaLabel
    type: string
    default: "'Pagination'"
    description: The aria-label of the root nav landmark
  - name: firstLabel
    type: string
    default: "'First page'"
    description: The first-page button's aria-label
  - name: prevLabel
    type: string
    default: "'Previous page'"
    description: The previous-page button's aria-label
  - name: nextLabel
    type: string
    default: "'Next page'"
    description: The next-page button's aria-label
  - name: lastLabel
    type: string
    default: "'Last page'"
    description: The last-page button's aria-label
---
::

### Events

::TuffPropsTable
---
rows:
  - name: update:currentPage
    type: '(page: number) => void'
    default: '-'
    description: Current-page update (v-model)
  - name: pageChange
    type: '(page: number) => void'
    default: '-'
    description: Fires when the user moves to another page
  - name: update:pageSize
    type: '(size: number) => void'
    default: '-'
    description: Page-size update (v-model) when the reader picks another size; the page is unchanged
  - name: pageSizeChange
    type: '(size: number) => void'
    default: '-'
    description: Fires when the reader picks another page size
---
::

### Slots

::TuffPropsTable
---
rows:
  - name: info
    type: '{ currentPage: number, totalPages: number, total?: number }'
    default: '-'
    description: Custom page info area
---
::

## Overview

- The page count comes from `total` and `pageSize`; when `total` is omitted or 0, `totalPages` is used.
- When `currentPage` falls out of range, the component emits `update:currentPage` with the nearest valid page.
- The active page button has `aria-current="page"`; previous, next, first, and last buttons have readable `aria-label`s and disable at the boundaries.
- The size selector renders only with valid `pageSizes`, and its combobox is named by the visible label through `aria-labelledby`; otherwise the DOM is unchanged.
- Picking a different size emits `update:pageSize`, then `pageSizeChange`, and leaves the page alone.
- The on-demand style plugin loads the selector's styles too; manual style imports also need `select/style.css` and its dependencies.

## Technologies

- Source: `packages/tuffex/packages/components/src/pagination/`.

<TuffDocSourceLink />
