
Creating Pagination in Nuxt 3
NuxtHi, I’m Lovefield.
This article is about pagination. Pagination is an essential feature for any page that displays posts or structured information. Because of its importance, it is considered a fundamental functionality.
First, create a Pagination.vue file inside the components folder.
Pagination.vue
HTML, XML
<template>
<div class=”pagination”></div>
</template>
<script setup lang=”ts”>
</script>
To calculate pagination details, we need the currently active page and the total number of pages. We use defineProps to receive this information. Additionally, pagination should only be displayed if the total number of pages is greater than 1.
We also need to receive the URL information required for navigation. The URL is provided in the format "/board/$", where $ will be replaced with the page number.
HTML, XML
<template>
<div v-if="props.totalPage > 1" class=”pagination”></div>
</template>
<script setup lang=”ts”>
interface PagenationData {
url: string;
active: number;
totalPage: number;
}
const props = defineProps<PagenationData>();
</script>
Define variables to store the computed pagination data and ensure that calculations are performed whenever totalPage changes or when the component is loaded. This can be achieved using Vue's computed properties or watch to reactively update the pagination details.
TypeScript
const step = ref<number>(0);
const totalStep = ref<number>(0);
const startNumber = ref<number>(1);
const numberList = ref<number[]>([]);
const itemCount: number = 5;
function init(): void {
}
init();
watch(
() => props.totalPage,
() => {
init();
}
);
Assuming that the pagination displays numbers from 1 to 5, this corresponds to the value of itemCount. In this case, the range from 1 to 5 is considered a single step. Based on the active page, the appropriate step is determined, and the necessary values are computed to render the pagination correctly on the screen.
TypeScript
function init(): void {
const stepData: number = Math.ceil(props.active / itemCount);
const totalStepData: number = Math.ceil(props.totalPage / itemCount);
const startNumberData: number = stepData * itemCount - (itemCount - 1);
const numberListData: number[] = [];
for (let i: number = startNumberData; i < startNumberData + itemCount; i += 1) {
if (i > props.totalPage) {
break;
}
numberListData.push(i);
}
step.value = stepData;
totalStep.value = totalStepData;
startNumber.value = startNumberData;
numberList.value = numberListData;
}
Since all the necessary values for rendering have been calculated, it's time to structure the rendering. The pagination will include buttons to navigate to the previous and next steps, as well as buttons for pages 1 through 5. Each button will be assigned the movePageEvent function to ensure that clicking it redirects to the corresponding page.
HTML, XML
<template>
<div v-if="props.totalPage > 1" class="pagination">
<button class="link" :disabled="step === 1" @click="movePageEvent(startNumber - 1)"><</button>
<button
v-for="(item, count) in numberList"
class="link"
:class="{ '--active': item === props.active }"
:key="`btn-page${count}`"
@click="movePageEvent(item)"
>
{{ item }}
</button>
<button class="link" :disabled="step === totalStep" @click="movePageEvent(startNumber + itemCount)">
>
</button>
</div>
</template>
Now, let's write the function to handle page navigation. This function will replace the $ in the provided URL with the selected page number and redirect the user accordingly.
TypeScript
const route = useRoute();
async function movePageEvent(idx: number): Promise<void> {
await navigateTo({
path: props.url.replace("$", String(idx)),
query: route.query,
replace: true,
});
}
Once the component is complete, you can now use it as follows:
HTML, XML
<Pagination url="/board/free/$`" :active="1" :total="50" />
Here’s the final version of the Pagination component code.
Pagination.vue
HTML, XML
<template>
<div v-if="props.totalPage > 1" class="pagination">
<button class="link" :disabled="step === 1" @click="movePageEvent(startNumber - 1)"><</button>
<button
v-for="(item, count) in numberList"
class="link"
:class="{ '--active': item === props.active }"
:key="`btn-page${count}`"
@click="movePageEvent(item)"
>
{{ item }}
</button>
<button class="link" :disabled="step === totalStep" @click="movePageEvent(startNumber + itemCount)">
>
</button>
</div>
</template>
<script setup lang="ts">
interface PagenationData {
url: string;
active: number;
totalPage: number;
}
const route = useRoute();
const props = defineProps<PagenationData>();
const step = ref<number>(0);
const totalStep = ref<number>(0);
const startNumber = ref<number>(1);
const numberList = ref<number[]>([]);
const itemCount: number = 5;
function init(): void {
const stepData: number = Math.ceil(props.active / itemCount);
const totalStepData: number = Math.ceil(props.totalPage / itemCount);
const startNumberData: number = stepData * itemCount - (itemCount - 1);
const numberListData: number[] = [];
for (let i: number = startNumberData; i < startNumberData + itemCount; i += 1) {
if (i > props.totalPage) {
break;
}
numberListData.push(i);
}
step.value = stepData;
totalStep.value = totalStepData;
startNumber.value = startNumberData;
numberList.value = numberListData;
}
init();
async function movePageEvent(idx: number): Promise<void> {
await navigateTo({
path: props.url.replace("$", String(idx)),
query: route.query,
replace: true,
});
}
watch(
() => props.totalPage,
() => {
init();
}
);
</script>
Previous Article
Candidates Ask, Companies Answer: The Importance of Reverse Interviews
This is an article about the reason for the reverse interview and how to conduct the reverse interview.
Next Article
Configuring Multilingualism in Nuxt
Describes how to implement multilingualism without i18n in Nuxt.