Overview
The Pagination component (mt-pagination) provides navigation controls for paginated content. It includes first page, previous page, next page, and last page buttons, along with a direct page number input and an information display showing the current range of items.
Import
import MtPagination from '@/components/table-and-list/mt-pagination/mt-pagination.vue';
Props
The currently active page number (1-indexed)
Number of items to display per page
Total number of items across all pages
Events
change-current-page
(pageNumber: number) => void
Emitted when the user navigates to a different page. Returns the new page number
Usage Examples
<template>
<div>
<div class="items-list">
<div v-for="item in paginatedItems" :key="item.id">
{{ item.name }}
</div>
</div>
<mt-pagination
:current-page="currentPage"
:limit="itemsPerPage"
:total-items="totalItems"
@change-current-page="handlePageChange"
/>
</div>
</template>
<script setup>
import { ref, computed } from 'vue';
import MtPagination from '@/components/table-and-list/mt-pagination/mt-pagination.vue';
const currentPage = ref(1);
const itemsPerPage = ref(20);
const totalItems = ref(150);
const allItems = ref([
// Your items array
]);
const paginatedItems = computed(() => {
const start = (currentPage.value - 1) * itemsPerPage.value;
const end = start + itemsPerPage.value;
return allItems.value.slice(start, end);
});
const handlePageChange = (page) => {
currentPage.value = page;
// Optional: Scroll to top
window.scrollTo({ top: 0, behavior: 'smooth' });
};
</script>
<template>
<div>
<div v-if="loading">Loading...</div>
<div v-else class="data-grid">
<div v-for="item in items" :key="item.id">
{{ item.title }}
</div>
</div>
<mt-pagination
:current-page="pagination.page"
:limit="pagination.limit"
:total-items="pagination.total"
@change-current-page="loadPage"
/>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import MtPagination from '@/components/table-and-list/mt-pagination/mt-pagination.vue';
const items = ref([]);
const loading = ref(false);
const pagination = ref({
page: 1,
limit: 25,
total: 0
});
const loadPage = async (page) => {
loading.value = true;
try {
const response = await fetch(
`/api/items?page=${page}&limit=${pagination.value.limit}`
);
const data = await response.json();
items.value = data.items;
pagination.value.page = page;
pagination.value.total = data.total;
} catch (error) {
console.error('Failed to load page:', error);
} finally {
loading.value = false;
}
};
onMounted(() => {
loadPage(1);
});
</script>
<template>
<div>
<div class="controls">
<label>
Items per page:
<select v-model.number="pageSize" @change="handlePageSizeChange">
<option :value="10">10</option>
<option :value="25">25</option>
<option :value="50">50</option>
<option :value="100">100</option>
</select>
</label>
</div>
<div class="items">
<!-- Your items -->
</div>
<mt-pagination
:current-page="currentPage"
:limit="pageSize"
:total-items="totalItems"
@change-current-page="currentPage = $event"
/>
</div>
</template>
<script setup>
import { ref } from 'vue';
import MtPagination from '@/components/table-and-list/mt-pagination/mt-pagination.vue';
const currentPage = ref(1);
const pageSize = ref(25);
const totalItems = ref(500);
const handlePageSizeChange = () => {
// Reset to first page when changing page size
currentPage.value = 1;
};
</script>
<template>
<mt-card title="Customer List">
<table class="data-table">
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>Status</th>
<th>Created</th>
</tr>
</thead>
<tbody>
<tr v-for="customer in paginatedCustomers" :key="customer.id">
<td>{{ customer.name }}</td>
<td>{{ customer.email }}</td>
<td>{{ customer.status }}</td>
<td>{{ formatDate(customer.createdAt) }}</td>
</tr>
</tbody>
</table>
<template #footer>
<mt-pagination
:current-page="currentPage"
:limit="limit"
:total-items="customers.length"
@change-current-page="currentPage = $event"
/>
</template>
</mt-card>
</template>
<script setup>
import { ref, computed } from 'vue';
import MtCard from '@/components/layout/mt-card/mt-card.vue';
import MtPagination from '@/components/table-and-list/mt-pagination/mt-pagination.vue';
const currentPage = ref(1);
const limit = ref(20);
const customers = ref([/* customer data */]);
const paginatedCustomers = computed(() => {
const start = (currentPage.value - 1) * limit.value;
return customers.value.slice(start, start + limit.value);
});
const formatDate = (date) => {
return new Date(date).toLocaleDateString();
};
</script>
<template>
<div>
<div class="toolbar">
<input
v-model="searchQuery"
type="search"
placeholder="Search products..."
@input="handleSearch"
/>
</div>
<div class="products">
<div v-for="product in displayedProducts" :key="product.id">
{{ product.name }}
</div>
</div>
<mt-pagination
:current-page="currentPage"
:limit="limit"
:total-items="filteredProducts.length"
@change-current-page="currentPage = $event"
/>
</div>
</template>
<script setup>
import { ref, computed } from 'vue';
import MtPagination from '@/components/table-and-list/mt-pagination/mt-pagination.vue';
const searchQuery = ref('');
const currentPage = ref(1);
const limit = ref(30);
const allProducts = ref([
// All products
]);
const filteredProducts = computed(() => {
if (!searchQuery.value) return allProducts.value;
return allProducts.value.filter(product =>
product.name.toLowerCase().includes(searchQuery.value.toLowerCase())
);
});
const displayedProducts = computed(() => {
const start = (currentPage.value - 1) * limit.value;
return filteredProducts.value.slice(start, start + limit.value);
});
const handleSearch = () => {
// Reset to first page when searching
currentPage.value = 1;
};
</script>
<template>
<div>
<div class="content">
<!-- Your paginated content -->
</div>
<mt-pagination
:current-page="currentPage"
:limit="limit"
:total-items="totalItems"
@change-current-page="handlePageChange"
/>
</div>
</template>
<script setup>
import { ref, watch, onMounted } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import MtPagination from '@/components/table-and-list/mt-pagination/mt-pagination.vue';
const route = useRoute();
const router = useRouter();
const currentPage = ref(1);
const limit = ref(20);
const totalItems = ref(1000);
const handlePageChange = (page) => {
currentPage.value = page;
router.push({
query: { ...route.query, page }
});
};
// Initialize from URL on mount
onMounted(() => {
const pageFromUrl = parseInt(route.query.page as string);
if (pageFromUrl && pageFromUrl > 0) {
currentPage.value = pageFromUrl;
}
});
</script>
Features
Navigation Buttons
- First Page: Jump to the first page
- Previous Page: Go to the previous page
- Next Page: Go to the next page
- Last Page: Jump to the last page
Direct Page Input
Users can type a page number directly into the input field to jump to a specific page.
Information Display
Shows the current range of items being displayed (e.g., “1-20 of 150”).
Automatic Page Reset
When the limit prop changes, the component automatically resets to page 1.
Accessibility
- All buttons include visually hidden text for screen readers
- Icons have
aria-hidden="true" to prevent duplicate announcements
- Buttons are properly disabled when at boundaries (first/last page)
- Page input includes an accessible label
Calculated Behavior
- Total pages:
Math.ceil(totalItems / limit)
- First visible item:
(currentPage - 1) * limit + 1
- Last visible item:
Math.min(limit * currentPage, totalItems)
- First page button disabled when
currentPage === 1
- Last page button disabled when
currentPage === totalPages
Source
View the component source at /home/daytona/workspace/source/packages/component-library/src/components/table-and-list/mt-pagination/mt-pagination.vue:1