MeldUI

Data Pagination

Pagination toolbar for tables, grids, and lists — first/last/prev/next buttons with a page-size selector and "Page X of Y" info.

Overview

<DataPagination> is the toolbar style of pagination used by <DataTable>’s footer. It’s a focused composite for data-listy views: first / previous / next / last buttons, a page-size dropdown, and a “Page X of Y” indicator.

Unlike <Pagination> — which renders a page-number link bar (e.g. 1 · 2 · [3] · … · 10) — <DataPagination> favours compact controls over enumerating pages. It’s the right pick when the dataset has a large or unknown number of pages.

<DataTable> renders a <DataPagination> internally when enable-pagination is set. You can also use it standalone with any custom view (cards, grids, virtualized lists) by binding the same v-model shape.

V-model contract

import type { PaginationState } from '@meldui/vue'

const pagination = ref<PaginationState>({ pageIndex: 0, pageSize: 10 })
<DataPagination
  v-model:pagination="pagination"
  :page-count="totalPages"
  :total-rows="totalRecords"
/>

pageIndex is 0-indexed (TanStack convention). Convert to 1-indexed when talking to a server that expects ?page=N.

Props

PropTypeDefaultDescription
paginationPaginationStaterequiredv-model target — { pageIndex, pageSize }
pageCountnumberrequiredTotal number of pages
totalRowsnumberTotal record count. Required when showSelectedCount is on
pageSizeOptionsnumber[][10, 20, 30, 40, 50]Choices in the page-size dropdown
showPageSizeSelectorbooleantrueToggle the page-size dropdown
showPageInfobooleantrueToggle the “Page X of Y” indicator
showSelectedCountbooleanfalseToggle the “X of Y selected” indicator
selectedCountnumber0Count to show when showSelectedCount is true

Emits

EventPayloadDescription
update:paginationPaginationStateFires when the user changes page or page size

Standalone recipes

Card grid

A typical content grid: bind the same v-model:pagination shape <DataTable> uses, derive pageCount from the total record count divided by pagination.pageSize, and slice the current page client-side (or fetch it server-side).

Compact list

Hide the page-size selector with :show-page-size-selector="false" when the list has a fixed cadence — ledgers, transaction tables, timelines.

Selected row count

Pair show-selected-count with a selectedCount you maintain in the parent. The indicator appears on the left side of the toolbar as X of Y row(s) selected — and only renders when all three conditions hold: showSelectedCount is true, selectedCount > 0, and totalRows is provided. Track the selection set in the parent (not the page-local rows) so the count survives page changes.

<DataPagination
  v-model:pagination="pagination"
  :page-count="pageCount"
  :total-rows="totalRows"
  :selected-count="selectedIds.size"
  show-selected-count
/>

For tables, <DataTable :enable-row-selection> exposes a selectedIds ref (full set across all pages) via defineExpose — bind that count directly to :selected-count.

Standalone usage

When you don’t want a full table but still need pagination — for example a card grid driven by server-side data — bind the same pagination ref the table would have used.

<script setup>
import { ref, watch } from 'vue'
import { DataPagination, type PaginationState } from '@meldui/vue'

const pagination = ref<PaginationState>({ pageIndex: 0, pageSize: 12 })
const items = ref<Item[]>([])
const pageCount = ref(1)
const totalRows = ref(0)

async function fetchPage() {
  const res = await api.list({ page: pagination.value.pageIndex + 1, perPage: pagination.value.pageSize })
  items.value = res.data
  pageCount.value = res.meta.total_pages
  totalRows.value = res.meta.total
}

watch(pagination, fetchPage, { deep: true })
fetchPage()
</script>

<template>
  <div class="grid grid-cols-3 gap-4">
    <Card v-for="item in items" :key="item.id">{{ item.name }}</Card>
  </div>
  <DataPagination v-model:pagination="pagination" :page-count="pageCount" :total-rows="totalRows" />
</template>

Inside <DataTable>

When <DataTable enable-pagination v-model:pagination="…"> is set, the table renders a <DataPagination> in its footer using the same v-model ref. To customise the footer, use the #pagination slot — you can replace it with your own layout (the composite is still available to drop in):

<DataTable enable-pagination v-model:pagination="pagination" :page-count="pageCount">
  <template #pagination="{ pagination: p, pageCount: pc, totalRows: tr }">
    <div class="flex items-center justify-between">
      <span class="text-xs text-muted-foreground">Custom footer</span>
      <DataPagination
        :pagination="p"
        :page-count="pc"
        :total-rows="tr"
        :show-page-size-selector="false"
        @update:pagination="(v) => pagination = v"
      />
    </div>
  </template>
</DataTable>

See also

  • Pagination — page-number link bar style. Use this for search results, blog indexes, or any content pagination where enumerating pages is useful.