
Pagination
A headless component for creating page navigation with proper ARIA support.
Usage
The Pagination component provides a compound component pattern for building page navigation interfaces. It uses the usePagination→ and useOverflow→ composable internally.
Anatomy
<script lang="ts" setup>
import { Pagination } from '@vuetify/v0'
</script>
<template>
<Pagination.Root v-slot="{ items }">
<Pagination.First />
<Pagination.Prev />
<template v-for="(item, index) in items" :key="index">
<Pagination.Ellipsis v-if="item.type === 'ellipsis'" />
<Pagination.Item v-else :value="item.value" />
</template>
<Pagination.Next />
<Pagination.Last />
</Pagination.Root>
</template>For responsive sizing to work accurately, all pagination buttons must have the same width. The component measures a sample button and uses that width to calculate how many buttons fit. If buttons have variable widths (e.g., single-digit “1” vs double-digit “50”), the calculation will be inaccurate and items may overflow or leave excess space.
API
| Composable | Description |
|---|---|
| useOverflow→ | Used for responsive auto-sizing of visible page buttons |
| usePagination→ | The underlying composable used by Pagination |
PaginationRoot
The root component that manages pagination state and provides context to child components. Supports responsive auto-sizing based on container width.
Props
Extends
AtomProps:interface PaginationRootProps extends AtomProps { as?: DOMElement | null renderless?: boolean namespace?: string size?: number totalVisible?: number itemsPerPage?: number ellipsis?: string | false }as: Element type to render (default:'nav')renderless: If true, renders no wrapper elementnamespace: Namespace for dependency injection (default:'v0:pagination')size: Total number of items to paginatetotalVisible: Maximum number of visible page buttons. If undefined, auto-calculates based on container widthitemsPerPage: Number of items per page (default:10)ellipsis: Character used for ellipsis, orfalseto disable ellipsis (default:'...')
v-model
v-model: numberBinds to the current page number (1-indexed).
Events
Event Payload Description update:model-valuenumberEmitted when the current page changes Example:
<template> <Pagination.Root v-model="page" :size="200" @update:model-value="onPageChange" > <!-- pagination controls --> </Pagination.Root> </template>Slot Props
interface PaginationRootSlotProps { page: number size: number pages: number itemsPerPage: number items: PaginationTicket[] pageStart: number pageStop: number isFirst: boolean isLast: boolean first: () => void last: () => void next: () => void prev: () => void select: (page: number) => void attrs: { 'aria-label': string } }page: Current page number (1-indexed)size: Total number of itemspages: Total number of pages (computed from size / itemsPerPage)itemsPerPage: Items per pageitems: Array of page items for rendering (includes ellipsis)pageStart: Start index of items on current page (0-indexed)pageStop: End index of items on current page (exclusive)isFirst: Whether on the first pageisLast: Whether on the last pagefirst,last,next,prev,select: Navigation methodsattrs: Object containing attributes to bind to the root element
PaginationItem
Individual page number button.
Props
Extends
AtomProps:interface PaginationItemProps extends AtomProps { as?: DOMElement | null renderless?: boolean namespace?: string value: number disabled?: boolean id?: string }as: Element type to render (default:'button')renderless: If true, renders no wrapper elementnamespace: Namespace for retrieving pagination context (default:'v0:pagination')value: Page number this item represents (required)disabled: Override disabled stateid: Unique identifier for registration (auto-generated if not provided)
Slot Props
interface PaginationItemSlotProps { page: number isSelected: boolean isDisabled: boolean select: () => void attrs: { 'aria-label': string 'aria-current': 'page' | undefined 'data-selected': true | undefined 'data-disabled': true | undefined 'disabled': boolean | undefined 'type': 'button' | undefined 'onClick': () => void } }page: Page numberisSelected: Whether this page is currently selectedisDisabled: Whether this item is disabledselect: Go to this pageattrs: Object containing all bindable attributes including ARIA attributes and event handlers
Data Attributes
Attribute Description data-selectedPresent when this page is currently selected data-disabledPresent when this item is disabled Accessibility
aria-current="page"when selectedaria-labelwith localized text (e.g., “Go to page 5” or “Page 5, current page”)
Example
<script lang="ts" setup> import { Pagination } from '@vuetify/v0' </script> <template> <!-- Simple usage --> <Pagination.Item :value="1">1</Pagination.Item> <!-- With slot props --> <Pagination.Item :value="2" v-slot="{ isSelected }"> <span :class="{ 'font-bold': isSelected }">2</span> </Pagination.Item> <!-- With data attributes for styling --> <Pagination.Item :value="3" class="data-[selected]:bg-blue-500 data-[selected]:text-white" > 3 </Pagination.Item> <!-- Renderless mode with attrs spread --> <Pagination.Item :value="4" renderless v-slot="{ attrs }"> <MyCustomButton v-bind="attrs">4</MyCustomButton> </Pagination.Item> </template>
PaginationEllipsis
Displays ellipsis to indicate hidden page numbers.
Props
Extends
AtomProps:interface PaginationEllipsisProps extends AtomProps { as?: DOMElement | null renderless?: boolean namespace?: string ellipsis?: string id?: string }as: Element type to render (default:'span')renderless: If true, renders no wrapper elementnamespace: Namespace for retrieving pagination context (default:'v0:pagination')ellipsis: Override the ellipsis character from contextid: Unique identifier for registration (auto-generated if not provided)
Slot Props
interface PaginationEllipsisSlotProps { ellipsis: string | false attrs: { 'aria-hidden': 'true' } }ellipsis: The ellipsis character, orfalseif disabledattrs: Object containing attributes to bind to the element
Note: When
ellipsis: falseis set on PaginationRoot, no ellipsis items are generated, so this component won’t render.Accessibility
aria-hidden="true"to hide from screen readers
Example
<script lang="ts" setup> import { Pagination } from '@vuetify/v0' </script> <template> <!-- Uses default "..." from Root --> <Pagination.Ellipsis /> <!-- Override ellipsis character --> <Pagination.Ellipsis ellipsis="…" /> <!-- Custom rendering with slot props --> <Pagination.Ellipsis v-slot="{ ellipsis, attrs }"> <span v-bind="attrs" class="px-2">{{ ellipsis }}</span> </Pagination.Ellipsis> </template>
PaginationFirst
Button to navigate to the first page.
Props
Extends
AtomProps:interface PaginationFirstProps extends AtomProps { as?: DOMElement | null renderless?: boolean namespace?: string disabled?: boolean id?: string }as: Element type to render (default:'button')renderless: If true, renders no wrapper elementnamespace: Namespace for retrieving pagination context (default:'v0:pagination')disabled: Override disabled state (defaults toisFirst)id: Unique identifier for registration (auto-generated if not provided)
Slot Props
interface PaginationFirstSlotProps { isDisabled: boolean first: () => void attrs: { 'aria-label': string 'aria-disabled': boolean | undefined 'data-disabled': true | undefined 'disabled': boolean | undefined 'type': 'button' | undefined 'onClick': () => void } }isDisabled: Whether the button is disabledfirst: Go to first pageattrs: Object containing all bindable attributes including ARIA attributes and event handlers
Data Attributes
Attribute Description data-disabledPresent when the button is disabled Accessibility
aria-labelwith localized text (e.g., “Go to first page”)aria-disabledwhen disabled (for non-button elements)- Native
disabledattribute for button elements
Example
<script lang="ts" setup> import { Pagination } from '@vuetify/v0' </script> <template> <!-- Simple usage --> <Pagination.First>«</Pagination.First> <!-- With slot props --> <Pagination.First v-slot="{ isDisabled }"> <span :class="{ 'opacity-50': isDisabled }">«</span> </Pagination.First> <!-- With data attributes for styling --> <Pagination.First class="data-[disabled]:opacity-50"> First </Pagination.First> <!-- Renderless mode with attrs spread --> <Pagination.First renderless v-slot="{ attrs }"> <MyCustomButton v-bind="attrs">«</MyCustomButton> </Pagination.First> </template>
PaginationPrev
Button to navigate to the previous page.
Props
Extends
AtomProps:interface PaginationPrevProps extends AtomProps { as?: DOMElement | null renderless?: boolean namespace?: string disabled?: boolean id?: string }as: Element type to render (default:'button')renderless: If true, renders no wrapper elementnamespace: Namespace for retrieving pagination context (default:'v0:pagination')disabled: Override disabled state (defaults toisFirst)id: Unique identifier for registration (auto-generated if not provided)
Slot Props
interface PaginationPrevSlotProps { isDisabled: boolean prev: () => void attrs: { 'aria-label': string 'aria-disabled': boolean | undefined 'data-disabled': true | undefined 'disabled': boolean | undefined 'type': 'button' | undefined 'onClick': () => void } }isDisabled: Whether the button is disabledprev: Go to previous pageattrs: Object containing all bindable attributes including ARIA attributes and event handlers
Data Attributes
Attribute Description data-disabledPresent when the button is disabled Accessibility
aria-labelwith localized text (e.g., “Go to previous page”)aria-disabledwhen disabled (for non-button elements)- Native
disabledattribute for button elements
Example
<script lang="ts" setup> import { Pagination } from '@vuetify/v0' </script> <template> <!-- Simple usage --> <Pagination.Prev>‹</Pagination.Prev> <!-- With slot props --> <Pagination.Prev v-slot="{ isDisabled }"> <span :class="{ 'opacity-50': isDisabled }">‹</span> </Pagination.Prev> <!-- With data attributes for styling --> <Pagination.Prev class="data-[disabled]:opacity-50"> Prev </Pagination.Prev> <!-- Renderless mode with attrs spread --> <Pagination.Prev renderless v-slot="{ attrs }"> <MyCustomButton v-bind="attrs">‹</MyCustomButton> </Pagination.Prev> </template>
PaginationNext
Button to navigate to the next page.
Props
Extends
AtomProps:interface PaginationNextProps extends AtomProps { as?: DOMElement | null renderless?: boolean namespace?: string disabled?: boolean id?: string }as: Element type to render (default:'button')renderless: If true, renders no wrapper elementnamespace: Namespace for retrieving pagination context (default:'v0:pagination')disabled: Override disabled state (defaults toisLast)id: Unique identifier for registration (auto-generated if not provided)
Slot Props
interface PaginationNextSlotProps { isDisabled: boolean next: () => void attrs: { 'aria-label': string 'aria-disabled': boolean | undefined 'data-disabled': true | undefined 'disabled': boolean | undefined 'type': 'button' | undefined 'onClick': () => void } }isDisabled: Whether the button is disablednext: Go to next pageattrs: Object containing all bindable attributes including ARIA attributes and event handlers
Data Attributes
Attribute Description data-disabledPresent when the button is disabled Accessibility
aria-labelwith localized text (e.g., “Go to next page”)aria-disabledwhen disabled (for non-button elements)- Native
disabledattribute for button elements
Example
<script lang="ts" setup> import { Pagination } from '@vuetify/v0' </script> <template> <!-- Simple usage --> <Pagination.Next>›</Pagination.Next> <!-- With slot props --> <Pagination.Next v-slot="{ isDisabled }"> <span :class="{ 'opacity-50': isDisabled }">›</span> </Pagination.Next> <!-- With data attributes for styling --> <Pagination.Next class="data-[disabled]:opacity-50"> Next </Pagination.Next> <!-- Renderless mode with attrs spread --> <Pagination.Next renderless v-slot="{ attrs }"> <MyCustomButton v-bind="attrs">›</MyCustomButton> </Pagination.Next> </template>
PaginationLast
Button to navigate to the last page.
Props
Extends
AtomProps:interface PaginationLastProps extends AtomProps { as?: DOMElement | null renderless?: boolean namespace?: string disabled?: boolean id?: string }as: Element type to render (default:'button')renderless: If true, renders no wrapper elementnamespace: Namespace for retrieving pagination context (default:'v0:pagination')disabled: Override disabled state (defaults toisLast)id: Unique identifier for registration (auto-generated if not provided)
Slot Props
interface PaginationLastSlotProps { isDisabled: boolean last: () => void attrs: { 'aria-label': string 'aria-disabled': boolean | undefined 'data-disabled': true | undefined 'disabled': boolean | undefined 'type': 'button' | undefined 'onClick': () => void } }isDisabled: Whether the button is disabledlast: Go to last pageattrs: Object containing all bindable attributes including ARIA attributes and event handlers
Data Attributes
Attribute Description data-disabledPresent when the button is disabled Accessibility
aria-labelwith localized text (e.g., “Go to last page”)aria-disabledwhen disabled (for non-button elements)- Native
disabledattribute for button elements
Example
<script lang="ts" setup> import { Pagination } from '@vuetify/v0' </script> <template> <!-- Simple usage --> <Pagination.Last>»</Pagination.Last> <!-- With slot props --> <Pagination.Last v-slot="{ isDisabled }"> <span :class="{ 'opacity-50': isDisabled }">»</span> </Pagination.Last> <!-- With data attributes for styling --> <Pagination.Last class="data-[disabled]:opacity-50"> Last </Pagination.Last> <!-- Renderless mode with attrs spread --> <Pagination.Last renderless v-slot="{ attrs }"> <MyCustomButton v-bind="attrs">»</MyCustomButton> </Pagination.Last> </template>
Recipes
Examples of common Pagination structures:
RouterLink
Use the as prop to render pagination items as RouterLink components.
<script lang="ts" setup>
import { Pagination } from '@vuetify/v0'
import { RouterLink } from 'vue-router'
</script>
<template>
<Pagination.Root :size="200" v-slot="{ items }">
<Pagination.First :as="RouterLink" to="...">«</Pagination.First>
<Pagination.Prev :as="RouterLink" to="...">‹</Pagination.Prev>
<template v-for="(item, index) in items" :key="index">
<Pagination.Ellipsis v-if="item.type === 'ellipsis'" />
<Pagination.Item
v-else
:as="RouterLink"
:value="item.value"
to="..."
>
{{ item.value }}
</Pagination.Item>
</template>
<Pagination.Next :as="RouterLink" to="...">›</Pagination.Next>
<Pagination.Last :as="RouterLink" to="...">»</Pagination.Last>
</Pagination.Root>
</template>Unordered list
Pagination example with nav, ul, and li elements.
<script lang="ts" setup>
import { Pagination } from '@vuetify/v0'
</script>
<template>
<Pagination.Root :size="200" v-slot="{ items }">
<ul>
<li>
<Pagination.First as="a" href="...">«</Pagination.First>
</li>
<li>
<Pagination.Prev as="a" href="...">‹</Pagination.Prev>
</li>
<li v-for="(item, index) in items" :key="index">
<Pagination.Ellipsis v-if="item.type === 'ellipsis'" />
<Pagination.Item
v-else
as="a"
:value="item.value"
href="..."
>
{{ item.value }}
</Pagination.Item>
</li>
<li>
<Pagination.Next as="a" href="...">›</Pagination.Next>
</li>
<li>
<Pagination.Last as="a" href="...">»</Pagination.Last>
</li>
</ul>
</Pagination.Root>
</template>