Using utility classes
<nav aria-label="page navigation"> <ul class="inline-flex flex-wrap gap-x-2 gap-y-1"> <li> <span class="flex w-12 aspect-square rounded-lg items-center justify-center text-gray-300 no-underline">«</span> </li> <li aria-current="page"> <span class="flex w-12 aspect-square rounded-lg items-center justify-center bg-blue-500 text-white">1</span> </li> <li> <a href="#" class="flex w-12 aspect-square rounded-lg items-center justify-center bg-gray-200 hover:bg-blue-200 no-underline">2</a> </li> <li> <a href="#" class="flex w-12 aspect-square rounded-lg items-center justify-center bg-gray-200 hover:bg-blue-200 no-underline">3</a> </li> <li> <span class="flex w-12 aspect-square rounded-lg items-center justify-center bg-gray-100 text-gray-400">...</span> </li> <li> <a href="#" class="flex w-12 aspect-square rounded-lg items-center justify-center hover:bg-blue-200 no-underline" aria-label="Next">»</a> </li> </ul></nav>
Create custom component classes
@layer components { .t-page-item { @apply flex w-12 aspect-square rounded-lg items-center justify-center bg-gray-200 hover:bg-blue-200 no-underline; } .t-page-item.active { @apply bg-blue-500 text-white; } .t-page-item.disabled { @apply bg-gray-100 text-gray-400; } .t-page-nav { @apply flex w-12 aspect-square rounded-lg items-center justify-center hover:bg-blue-200 no-underline; } .t-page-nav.disabled { @apply text-gray-300 no-underline hover:bg-inherit; }}
NOTE: Refer Tailwind Custom Base CSS and Tailwind Organize Components CSS File.
Usage
<nav aria-label="page navigation"> <ul class="t-inline-container"> <li> <span class="t-page-nav disabled">«</span> </li> <li aria-current="page"> <span class="t-page-item active">1</span> </li> <li> <a href="#" class="t-page-item">2</a> </li> <li> <a href="#" class="t-page-item">3</a> </li> <li> <span class="t-page-item disabled">...</span> </li> <li> <a href="#" class="t-page-nav" aria-label="Next">»</a> </li> </ul></nav>
Vue Component
<script setup>// import { VPagination } from 'vuetify/components/VPagination'const props = defineProps({ page: Number, length: Number, totalVisible: Number, to: Function})const range = (start, stop, step = 1) => Array(Math.ceil((stop - start) / step)).fill(start).map((x, y) => x + y * step)let totalVisible = props.totalVisible - 2if (totalVisible < 0) totalVisible = 3let total = Math.floor(totalVisible/2)let start = props.page - totalif (start <= 0) { total = 0 start = 1}total = totalVisible - totallet end = props.page + totalif (end > props.length) { end = props.length start -= total if (start <= 0) start = 1}let pages = range(start, end)if (pages[0] != 1) { pages.unshift(1) if (pages[1] != 2) { pages.splice(1, 0, 0); }}if (pages[pages.length - 1] != props.length) { pages.push(props.length) if (pages[pages.length - 2] != props.length-1) { pages.splice(pages.length-1, 0, 0); }}</script><template> <nav aria-label="page navigation"> <ul class="t-inline-container"> <li> <NuxtLink v-if="page !== 1" class="t-page-nav" aria-label="Previous" :to="to(page - 1)">«</NuxtLink> <span v-else class="t-page-nav disabled">«</span> </li> <li v-for="_page in pages" :key="_page" v-bind="page == _page ? { 'aria-current': 'page' } : {}"> <span v-if="page == _page" class="t-page-item active">{{ _page }}</span> <span v-else-if="_page == 0" class="t-page-item disabled">...</span> <NuxtLink v-else class="t-page-item" :to="to(_page)">{{ _page }}</NuxtLink> </li> <li> <NuxtLink v-if="page != length" class="t-page-nav" aria-label="Next" :to="to(page + 1)">»</NuxtLink> <span v-else class="t-page-nav disabled">»</span> </li> </ul> </nav></template>
Usage
<script setup>const pagination = { page: 10, length: 10, totalVisible: 5, to: (page) => { return `page/${page}` }}</script><template> <t-pagination v-bind="pagination"></t-pagination></template>