React Pagination - Getting Started

NOTE
Before you start the tutorial, ensure DevExtreme is installed in your application.

Pagination is a UI component that allows users to navigate through pages and change page size at runtime.

This tutorial explains how to add Pagination to a page and configure the component's core settings. It also covers implementing remote pagination. Colored cards are loaded each time a user switches pages or changes page size. The final result is displayed below:

Each section in this tutorial describes a single configuration step. You can also find the full source code in the following GitHub repository:

View on GitHub

Create Pagination

Add DevExtreme to your Vue application and use the following code to create a Pagination component:

App.vue
  • <template>
  • <DxPagination />
  • </template>
  •  
  • <script setup lang="ts">
  • import 'devextreme/dist/css/dx.light.css';
  • import { DxPagination } from 'devextreme-vue/pagination';
  • </script>

Configure Pagination

This tutorial step guides you through the basic Pagination setup.

Specify the following settings:

  • itemCount sets the total number of items. Pagination does not function properly without this setting.
  • pageIndex sets the initial page to display. This tutorial sets pageIndex to 3 (the default value is 1).
  • allowedPageSizes specifies page sizes available to users. Modify this list as needed. Include 'all' to allow users to display all items on one page. This tutorial uses the default value: [5, 10].
  • pageSize specifies the initial page size.

The following code snippet demonstrates how to apply the aforementioned settings:

App.vue
  • <template>
  • <DxPagination
  • :show-info="true"
  • :show-navigation-buttons="true"
  • v-model:page-index="pageIndex"
  • v-model:page-size="pageSize"
  • :item-count="total"
  • />
  • </template>
  •  
  • <script setup lang="ts">
  • // ...
  • import { ref } from 'vue';
  •  
  • const total = 100;
  • const pageSize = ref(5);
  • const pageIndex = ref(3);
  • </script>

Implement Remote Pagination

This section explains how to implement remote pagination. Client code generates a color list and requests a remote service for cards representing color entries on the screen. The pagination component helps users browse the resulting color cards.

Implementation can be broken down into three steps:

  1. Generate 100 hex codes.
  2. Fetch color cards from The Color API service when necessary:
    • On page load
    • On page size changes
    • On page index changes
  3. Display color cards obtained from the service.

Implement the first step. Generate 100 random pastel hex codes and add them to an array:

App.vue
colorService.ts
  • <template>
  • <!-- ... -->
  • </template>
  •  
  • <script setup lang="ts">
  • // ...
  • import { ref, onMounted } from 'vue';
  • import { getRandomPastelColor } from '../assets/colorService';
  •  
  • const hexCodes = ref<string[]>([]);
  •  
  • const generateHexCodes = () => {
  • for (let i = 0; i < total; i++) {
  • hexCodes.value.push(getRandomPastelColor());
  • }
  • };
  •  
  • onMounted(() => {
  • generateHexCodes();
  • });
  • </script>
  • export const getRandomPastelColor = (): string => {
  • const hue = Math.floor(Math.random() * 360);
  • const saturation = Math.random() * 0.4 + 0.2;
  • const brightness = Math.random() * 0.3 + 0.7;
  • return hsvToHex(hue, saturation, brightness);
  • };
  •  
  • const hsvToHex = (h: number, s: number, v: number): string => {
  • let r: number, g: number, b: number;
  • const i = Math.floor(h / 60) % 6;
  • const f = h / 60 - i;
  • const p = v * (1 - s);
  • const q = v * (1 - f * s);
  • const t = v * (1 - (1 - f) * s);
  •  
  • switch (i) {
  • case 0: [r, g, b] = [v, t, p]; break;
  • case 1: [r, g, b] = [q, v, p]; break;
  • case 2: [r, g, b] = [p, v, t]; break;
  • case 3: [r, g, b] = [p, q, v]; break;
  • case 4: [r, g, b] = [t, p, v]; break;
  • case 5: [r, g, b] = [v, p, q]; break;
  • default: throw new Error("Unexpected case in HSV to RGB conversion");
  • }
  •  
  • const toHex = (x: number): string => Math.round(x * 255).toString(16).padStart(2, '0');
  • return `${toHex(r)}${toHex(g)}${toHex(b)}`;
  • };

Fetch Data

The following code snippet demonstrates how to fetch data from the Color API:

colorService.ts
  • import axios from 'axios';
  •  
  • const apiEndpoint = 'https://www.thecolorapi.com/id?hex=';
  •  
  • export const fetchColorData = async (
  • hex: string,
  • ): Promise<{ name: string; image: string } | null> => {
  • try {
  • const response = await axios.get(`${apiEndpoint}${hex}`);
  • return {
  • name: response.data.name.value,
  • image: response.data.image.bare,
  • };
  • } catch (error) {
  • console.error(`Error fetching color for hex ${hex}:`, error);
  • return null;
  • }
  • };

Render Items

The render function determines the subset of cards to be displayed and populates the array with images and alt strings.

App.vue
  • <template>
  • <DxPagination ... />
  • <div id="cards">
  • <div v-for="color in visibleCards" :key="color.name">
  • <img :src="color.image" :alt="color.name" />
  • </div>
  • </div>
  • </template>
  •  
  • <script setup lang="ts">
  • // ...
  • interface Color {
  • image: string;
  • name: string;
  • }
  •  
  • const visibleCards = ref([] as Color[]);
  • const colorCache = new Map<string, Color>();
  •  
  • const fetchColorsForPage = async () => {
  • const startIndex = (pageIndex.value - 1) * pageSize.value;
  • const endIndex = startIndex + pageSize.value;
  • const hexSubset = hexCodes.value.slice(startIndex, endIndex);
  •  
  • const promises = hexSubset.map((hex) => {
  • if (colorCache.has(hex)) {
  • return Promise.resolve(colorCache.get(hex));
  • }
  • return fetchColorData(hex).then((color) => {
  • if (color) {
  • colorCache.set(hex, color);
  • }
  • return color;
  • });
  • });
  •  
  • try {
  • const results = await Promise.all(promises);
  • visibleCards.value = results.filter((color): color is Color => color !== null);
  • } catch (error) {
  • console.error('Error fetching colors:', error);
  • }
  • };
  •  
  • onMounted(() => {
  • // ...
  • fetchColorsForPage();
  • });
  • </script>
  •  
  • <style>
  • #cards {
  • display: flex;
  • justify-content: center;
  • flex-wrap: wrap;
  • }
  • </style>

Handle Page Size and Index Changes

The render function is called on every page index/size change:

App.vue
  • <template>
  • <DxPagination ...
  • @update:page-index="onPageIndexChange"
  • @update:page-size="onPageSizeChange"
  • />
  • <!-- ... -->
  • </template>
  •  
  • <script setup lang="ts">
  • // ...
  • const onPageIndexChange = (value: number) => {
  • pageIndex.value = value;
  • fetchColorsForPage();
  • };
  •  
  • const onPageSizeChange = (value: number) => {
  • pageSize.value = value;
  • fetchColorsForPage();
  • };
  • </script>

Integrate LoadPanel

The following code integrate a load panel into the application. The panel appears when the app requests card data from the remote service. This step is optional.

To integrate the DevExtreme LoadPanel component:

  1. Add a LoadPanel to the code.
  2. Display it before calling the render function.
  3. Hide it after render.
App.vue
  • <template>
  • <DxPagination ... />
  • <!-- ... -->
  • <DxLoadPanel
  • v-model:visible="loadPanelVisible"
  • :show-indicator="true"
  • :show-pane="true"
  • :hide-on-outside-click="false"
  • >
  • <DxPosition my="top" at="top" of="#cards" />
  • </DxLoadPanel>
  • </template>
  •  
  • <script setup lang="ts">
  • // ...
  • import { DxLoadPanel, DxPosition } from 'devextreme-vue/load-panel';
  •  
  • const loadPanelVisible = ref(false);
  •  
  • const fetchColorsForPage = async () => {
  • loadPanelVisible.value = true;
  • const startIndex = (pageIndex.value - 1) * pageSize.value;
  • const endIndex = startIndex + pageSize.value;
  • const hexSubset = hexCodes.value.slice(startIndex, endIndex);
  •  
  • const promises = hexSubset.map((hex) => {
  • // ...
  • });
  •  
  • try {
  • const results = await Promise.all(promises);
  • visibleCards.value = results.filter((color): color is Color => color !== null);
  • } catch (error) {
  • console.error('Error fetching colors:', error);
  • } finally {
  • loadPanelVisible.value = false;
  • }
  • };
  •  
  • // ...
  • </script>