Skip to content

Pagination

Page navigation with composable page links, previous/next buttons, and ellipsis indicators.

Import

ts
import {
  Pagination,
  PaginationContent,
  PaginationEllipsis,
  PaginationItem,
  PaginationLink,
  PaginationNext,
  PaginationNextIcon,
  PaginationPrevious,
  PaginationPreviousIcon,
  PaginationSummary,
} from '@heroui-vue/vue'

Usage

<template>
  <Pagination class="justify-center">
    <PaginationContent>
      <PaginationItem>
        <PaginationPrevious :is-disabled="page === 1" @press="page -= 1">
          <PaginationPreviousIcon />
          <span>Previous</span>
        </PaginationPrevious>
      </PaginationItem>
      <PaginationItem v-for="item in totalPages" :key="item">
        <PaginationLink :is-active="item === page" @press="page = item">
          {{ item }}
        </PaginationLink>
      </PaginationItem>
      <PaginationItem>
        <PaginationNext :is-disabled="page === totalPages" @press="page += 1">
          <span>Next</span>
          <PaginationNextIcon />
        </PaginationNext>
      </PaginationItem>
    </PaginationContent>
  </Pagination>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import {
  Pagination,
  PaginationContent,
  PaginationItem,
  PaginationLink,
  PaginationNext,
  PaginationNextIcon,
  PaginationPrevious,
  PaginationPreviousIcon,
} from '@heroui-vue/vue'

const page = ref(1)
const totalPages = 3
</script>

Anatomy

vue
<Pagination>
  <PaginationSummary>Showing 1-10 of 100 results</PaginationSummary>
  <PaginationContent>
    <PaginationItem>
      <PaginationPrevious>
        <PaginationPreviousIcon />
        <span>Previous</span>
      </PaginationPrevious>
    </PaginationItem>
    <PaginationItem>
      <PaginationLink active>1</PaginationLink>
    </PaginationItem>
    <PaginationItem>
      <PaginationEllipsis />
    </PaginationItem>
    <PaginationItem>
      <PaginationNext>
        <span>Next</span>
        <PaginationNextIcon />
      </PaginationNext>
    </PaginationItem>
  </PaginationContent>
</Pagination>

Sizes

sm
md
lg

<template>
  <div class="flex flex-col gap-6">
    <div v-for="size in sizes" :key="size" class="flex flex-col gap-2">
      <span class="text-xs font-medium text-muted capitalize">{{ size }}</span>
      <Pagination class="justify-center" :size="size">
        <PaginationContent>
          <PaginationItem>
            <PaginationPrevious :is-disabled="pages[size] === 1" @press="pages[size] -= 1">
              <PaginationPreviousIcon />
              <span>Previous</span>
            </PaginationPrevious>
          </PaginationItem>
          <PaginationItem v-for="item in totalPages" :key="item">
            <PaginationLink :is-active="item === pages[size]" @press="pages[size] = item">
              {{ item }}
            </PaginationLink>
          </PaginationItem>
          <PaginationItem>
            <PaginationNext :is-disabled="pages[size] === totalPages" @press="pages[size] += 1">
              <span>Next</span>
              <PaginationNextIcon />
            </PaginationNext>
          </PaginationItem>
        </PaginationContent>
      </Pagination>
    </div>
  </div>
</template>

<script setup lang="ts">
import { reactive } from 'vue'
import {
  Pagination,
  PaginationContent,
  PaginationItem,
  PaginationLink,
  PaginationNext,
  PaginationNextIcon,
  PaginationPrevious,
  PaginationPreviousIcon,
} from '@heroui-vue/vue'

const sizes = ['sm', 'md', 'lg'] as const
const totalPages = 3
const pages = reactive({ sm: 1, md: 1, lg: 1 })
</script>

With Ellipsis

<template>
  <div class="w-full max-w-2xs overflow-x-auto sm:max-w-full">
    <Pagination class="justify-center">
      <PaginationContent>
        <PaginationItem>
          <PaginationPrevious :is-disabled="page === 1" @press="page -= 1">
            <PaginationPreviousIcon />
            <span>Previous</span>
          </PaginationPrevious>
        </PaginationItem>
        <PaginationItem v-for="(item, index) in pageItems" :key="`${item}-${index}`">
          <PaginationEllipsis v-if="item === 'ellipsis'" />
          <PaginationLink v-else :is-active="item === page" @press="page = item">
            {{ item }}
          </PaginationLink>
        </PaginationItem>
        <PaginationItem>
          <PaginationNext :is-disabled="page === totalPages" @press="page += 1">
            <span>Next</span>
            <PaginationNextIcon />
          </PaginationNext>
        </PaginationItem>
      </PaginationContent>
    </Pagination>
  </div>
</template>

<script setup lang="ts">
import { computed, ref } from 'vue'
import {
  Pagination,
  PaginationContent,
  PaginationEllipsis,
  PaginationItem,
  PaginationLink,
  PaginationNext,
  PaginationNextIcon,
  PaginationPrevious,
  PaginationPreviousIcon,
} from '@heroui-vue/vue'

const page = ref(1)
const totalPages = 12

const pageItems = computed(() => {
  const pages: Array<number | 'ellipsis'> = [1]

  if (page.value > 3) pages.push('ellipsis')

  const start = Math.max(2, page.value - 1)
  const end = Math.min(totalPages - 1, page.value + 1)

  for (let item = start; item <= end; item += 1) {
    pages.push(item)
  }

  if (page.value < totalPages - 2) pages.push('ellipsis')
  pages.push(totalPages)

  return pages
})
</script>

Simple (Previous / Next)

<template>
  <Pagination class="w-full">
    <PaginationSummary>
      {{ startItem }} to {{ endItem }} of {{ totalItems }} invoices
    </PaginationSummary>
    <PaginationContent>
      <PaginationItem>
        <PaginationPrevious :is-disabled="page === 1" @press="page -= 1">
          <PaginationPreviousIcon />
          <span>Prev</span>
        </PaginationPrevious>
      </PaginationItem>
      <PaginationItem>
        <PaginationNext :is-disabled="page === totalPages" @press="page += 1">
          <span>Next</span>
          <PaginationNextIcon />
        </PaginationNext>
      </PaginationItem>
    </PaginationContent>
  </Pagination>
</template>

<script setup lang="ts">
import { computed, ref } from 'vue'
import {
  Pagination,
  PaginationContent,
  PaginationItem,
  PaginationNext,
  PaginationNextIcon,
  PaginationPrevious,
  PaginationPreviousIcon,
  PaginationSummary,
} from '@heroui-vue/vue'

const page = ref(1)
const totalPages = 10
const itemsPerPage = 5
const totalItems = 50
const startItem = computed(() => (page.value - 1) * itemsPerPage + 1)
const endItem = computed(() => Math.min(page.value * itemsPerPage, totalItems))
</script>

With Summary

<template>
  <Pagination class="w-full">
    <PaginationSummary>
      Showing {{ startItem }}-{{ endItem }} of {{ totalItems }} results
    </PaginationSummary>
    <PaginationContent>
      <PaginationItem>
        <PaginationPrevious :is-disabled="page === 1" @press="page -= 1">
          <PaginationPreviousIcon />
          <span>Previous</span>
        </PaginationPrevious>
      </PaginationItem>
      <PaginationItem v-for="(item, index) in pageItems" :key="`${item}-${index}`">
        <PaginationEllipsis v-if="item === 'ellipsis'" />
        <PaginationLink v-else :is-active="item === page" @press="page = item">
          {{ item }}
        </PaginationLink>
      </PaginationItem>
      <PaginationItem>
        <PaginationNext :is-disabled="page === totalPages" @press="page += 1">
          <span>Next</span>
          <PaginationNextIcon />
        </PaginationNext>
      </PaginationItem>
    </PaginationContent>
  </Pagination>
</template>

<script setup lang="ts">
import { computed, ref } from 'vue'
import {
  Pagination,
  PaginationContent,
  PaginationEllipsis,
  PaginationItem,
  PaginationLink,
  PaginationNext,
  PaginationNextIcon,
  PaginationPrevious,
  PaginationPreviousIcon,
  PaginationSummary,
} from '@heroui-vue/vue'

const page = ref(1)
const totalPages = 12
const itemsPerPage = 10
const totalItems = 120

const startItem = computed(() => (page.value - 1) * itemsPerPage + 1)
const endItem = computed(() => Math.min(page.value * itemsPerPage, totalItems))
const pageItems = computed(() => {
  const pages: Array<number | 'ellipsis'> = [1]
  if (page.value > 3) pages.push('ellipsis')
  for (let item = Math.max(2, page.value - 1); item <= Math.min(totalPages - 1, page.value + 1); item += 1) {
    pages.push(item)
  }
  if (page.value < totalPages - 2) pages.push('ellipsis')
  pages.push(totalPages)
  return pages
})
</script>

Custom Icons

<template>
  <Pagination class="justify-center">
    <PaginationContent>
      <PaginationItem>
        <PaginationPrevious :is-disabled="page === 1" @press="page -= 1">
          <PaginationPreviousIcon>
            <svg fill="none" viewBox="0 0 20 20">
              <path d="M12.5 5 7.5 10l5 5M8 10h8" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.8" />
            </svg>
          </PaginationPreviousIcon>
          <span>Back</span>
        </PaginationPrevious>
      </PaginationItem>
      <PaginationItem v-for="item in totalPages" :key="item">
        <PaginationLink :is-active="item === page" @press="page = item">
          {{ item }}
        </PaginationLink>
      </PaginationItem>
      <PaginationItem>
        <PaginationNext :is-disabled="page === totalPages" @press="page += 1">
          <span>Forward</span>
          <PaginationNextIcon>
            <svg fill="none" viewBox="0 0 20 20">
              <path d="m7.5 5 5 5-5 5M12 10H4" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.8" />
            </svg>
          </PaginationNextIcon>
        </PaginationNext>
      </PaginationItem>
    </PaginationContent>
  </Pagination>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import {
  Pagination,
  PaginationContent,
  PaginationItem,
  PaginationLink,
  PaginationNext,
  PaginationNextIcon,
  PaginationPrevious,
  PaginationPreviousIcon,
} from '@heroui-vue/vue'

const page = ref(1)
const totalPages = 3
</script>

Controlled

<template>
  <Pagination>
    <PaginationSummary>
      Showing {{ startItem }}-{{ endItem }} of {{ totalItems }} results
    </PaginationSummary>
    <PaginationContent>
      <PaginationItem>
        <PaginationPrevious :is-disabled="page === 1" @press="page -= 1">
          <PaginationPreviousIcon />
          <span>Previous</span>
        </PaginationPrevious>
      </PaginationItem>
      <PaginationItem v-for="(item, index) in pageItems" :key="`${item}-${index}`">
        <PaginationEllipsis v-if="item === 'ellipsis'" />
        <PaginationLink v-else :is-active="item === page" @press="page = item">
          {{ item }}
        </PaginationLink>
      </PaginationItem>
      <PaginationItem>
        <PaginationNext :is-disabled="page === totalPages" @press="page += 1">
          <span>Next</span>
          <PaginationNextIcon />
        </PaginationNext>
      </PaginationItem>
    </PaginationContent>
  </Pagination>
</template>

<script setup lang="ts">
import { computed, ref } from 'vue'
import {
  Pagination,
  PaginationContent,
  PaginationEllipsis,
  PaginationItem,
  PaginationLink,
  PaginationNext,
  PaginationNextIcon,
  PaginationPrevious,
  PaginationPreviousIcon,
  PaginationSummary,
} from '@heroui-vue/vue'

const page = ref(1)
const totalPages = 12
const itemsPerPage = 10
const totalItems = 120

const startItem = computed(() => (page.value - 1) * itemsPerPage + 1)
const endItem = computed(() => Math.min(page.value * itemsPerPage, totalItems))
const pageItems = computed(() => {
  const pages: Array<number | 'ellipsis'> = []

  if (totalPages <= 7) {
    for (let item = 1; item <= totalPages; item += 1) pages.push(item)
    return pages
  }

  pages.push(1)
  if (page.value > 3) pages.push('ellipsis')
  for (let item = Math.max(2, page.value - 1); item <= Math.min(totalPages - 1, page.value + 1); item += 1) {
    pages.push(item)
  }
  if (page.value < totalPages - 2) pages.push('ellipsis')
  pages.push(totalPages)
  return pages
})
</script>

Disabled

<template>
  <Pagination class="justify-center">
    <PaginationContent>
      <PaginationItem>
        <PaginationPrevious is-disabled @press="page -= 1">
          <PaginationPreviousIcon />
          <span>Previous</span>
        </PaginationPrevious>
      </PaginationItem>
      <PaginationItem v-for="item in totalPages" :key="item">
        <PaginationLink :is-active="item === page" @press="page = item">
          {{ item }}
        </PaginationLink>
      </PaginationItem>
      <PaginationItem>
        <PaginationNext is-disabled @press="page += 1">
          <span>Next</span>
          <PaginationNextIcon />
        </PaginationNext>
      </PaginationItem>
    </PaginationContent>
  </Pagination>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import {
  Pagination,
  PaginationContent,
  PaginationItem,
  PaginationLink,
  PaginationNext,
  PaginationNextIcon,
  PaginationPrevious,
  PaginationPreviousIcon,
} from '@heroui-vue/vue'

const page = ref(1)
const totalPages = 3
</script>

API

PropTypeDefaultDescription
size'sm' | 'md' | 'lg''md'Size of pagination controls
ariaLabelstring'Pagination'Accessible label for the navigation

PaginationLink, PaginationPrevious, and PaginationNext emit press with the click event.

Released under the Apache-2.0 License.