Skip to content

Pagination

Split large collections into digestible pages and let users navigate between them. Pagination automatically calculates page count from totalItems and pageSize, and intelligently truncates the page range with ellipses when there are too many pages to display at once.

ts
import { CoarPagination } from '@cocoar/vue-ui';

Basic Usage

Bind a reactive page number with v-model. This example paginates 100 items at 10 per page, yielding 10 pages.

Current page: 1

vue
<template>
  <div>
    <CoarPagination v-model="page" :total-items="100" :page-size="10" />
    <p style="margin-top: 8px; font-size: 13px; color: var(--coar-text-neutral-secondary);">Current page: {{ page }}</p>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { CoarPagination } from '@cocoar/vue-ui';

const page = ref(1);
</script>

Different Page Size

Adjust totalItems and pageSize to match your data. Here, 500 items at 25 per page produces 20 pages.

Current page: 3 / 20

vue
<template>
  <div>
    <CoarPagination v-model="page" :total-items="500" :page-size="25" />
    <p style="margin-top: 8px; font-size: 13px; color: var(--coar-text-neutral-secondary);">Current page: {{ page }} / 20</p>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { CoarPagination } from '@cocoar/vue-ui';

const page = ref(3);
</script>

Large Dataset

With 10,000 items, the page count reaches 1,000. The component automatically shows ellipsis markers so the control stays compact no matter how many pages exist.

Current page: 1 / 1000

vue
<template>
  <div>
    <CoarPagination v-model="page" :total-items="10000" :page-size="10" />
    <p style="margin-top: 8px; font-size: 13px; color: var(--coar-text-neutral-secondary);">Current page: {{ page }} / 1000</p>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { CoarPagination } from '@cocoar/vue-ui';

const page = ref(1);
</script>

With Table

The most common real-world pattern: pair pagination with a data table. A summary line shows the visible row range, and page controls sit alongside it in the table footer.

NameEmailRole
User 1user1@example.comUser
User 2user2@example.comAdmin
User 3user3@example.comUser
User 4user4@example.comAdmin
User 5user5@example.comUser
Showing 1–5 of 100
vue
<template>
  <div style="display: flex; flex-direction: column; gap: 16px;">
    <table style="width: 100%; border-collapse: collapse; font-size: 13px;">
      <thead>
        <tr>
          <th style="padding: 6px 12px; text-align: left; border-bottom: 1px solid var(--coar-border-neutral-secondary); font-weight: 600; color: var(--coar-text-neutral-secondary); background: var(--coar-background-neutral-secondary);">Name</th>
          <th style="padding: 6px 12px; text-align: left; border-bottom: 1px solid var(--coar-border-neutral-secondary); font-weight: 600; color: var(--coar-text-neutral-secondary); background: var(--coar-background-neutral-secondary);">Email</th>
          <th style="padding: 6px 12px; text-align: left; border-bottom: 1px solid var(--coar-border-neutral-secondary); font-weight: 600; color: var(--coar-text-neutral-secondary); background: var(--coar-background-neutral-secondary);">Role</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="i in 5" :key="i">
          <td style="padding: 6px 12px; border-bottom: 1px solid var(--coar-border-neutral-secondary);">User {{ (page - 1) * 5 + i }}</td>
          <td style="padding: 6px 12px; border-bottom: 1px solid var(--coar-border-neutral-secondary);">user{{ (page - 1) * 5 + i }}@example.com</td>
          <td style="padding: 6px 12px; border-bottom: 1px solid var(--coar-border-neutral-secondary);">{{ i % 2 === 0 ? 'Admin' : 'User' }}</td>
        </tr>
      </tbody>
    </table>
    <div style="display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 8px;">
      <span style="font-size: 13px; color: var(--coar-text-neutral-secondary);">
        Showing {{ (page - 1) * 5 + 1 }}–{{ Math.min(page * 5, 100) }} of 100
      </span>
      <CoarPagination v-model="page" :total-items="100" :page-size="5" />
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { CoarPagination } from '@cocoar/vue-ui';

const page = ref(1);
</script>

API

Props

PropTypeDefaultDescription
v-modelnumber1Current active page (1-indexed)
totalItemsnumberTotal number of items (required)
pageSizenumber10Items per page
maxVisiblePagesnumber5Maximum visible page buttons
showFirstLastbooleantrueShow first/last page nav buttons
disabledbooleanfalseDisable the pagination control

Events

EventPayloadDescription
pageChangednumberEmitted when the page changes

i18n Keys

These keys can be translated via @cocoar/vue-localization.

KeyDefault (English)Used as
coar.ui.pagination.firstPage'Go to first page'First page button aria-label
coar.ui.pagination.previousPage'Go to previous page'Previous page button aria-label
coar.ui.pagination.nextPage'Go to next page'Next page button aria-label
coar.ui.pagination.lastPage'Go to last page'Last page button aria-label
coar.ui.pagination.morePage'More pages'Ellipsis separator aria-label
coar.ui.pagination.goToPage'Go to page {page}'Page number button aria-label{page} is replaced with the page number
coar.ui.pagination.nav'Pagination'Nav element aria-label

Released under the Apache-2.0 License.