Vue 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 React application and use the following code to create a Pagination component:

App.tsx
  • import React from 'react';
  • import 'devextreme/dist/css/dx.light.css';
  • import { Pagination } from 'devextreme-react/pagination';
  •  
  • function App(): JSX.Element {
  • return (
  • <Pagination />
  • );
  • }
  •  
  • export default App;

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.tsx
  • import React, { useState } from 'react';
  • // ...
  • const total = 100;
  •  
  • function App(): JSX.Element {
  • const [pageSize, setPageSize] = useState<number>(5);
  • const [pageIndex, setPageIndex] = useState<number>(3);
  • return (
  • <Pagination
  • showInfo={true}
  • showNavigationButtons={true}
  • pageIndex={pageIndex}
  • pageSize={pageSize}
  • itemCount={total}
  • />
  • );
  • }
  •  
  • export default App;

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.tsx
colorService.ts
  • import React, { useState, useEffect } from 'react';
  • import { getRandomPastelColor } from './colorService';
  • // ...
  •  
  • function App(): JSX.Element {
  • // ...
  • const hexCodes = useRef<string[]>([]);
  •  
  • useEffect(() => {
  • for (let i = 0; i < total; i++) {
  • hexCodes.current.push(getRandomPastelColor());
  • }
  • }, []);
  •  
  • return (
  • <!-- ... -->
  • );
  • }
  •  
  • export default App;
  • 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");
  • }
  •  
  • function toHex(x: number): string {
  • if (isNaN(x) || x < 0 || x > 1) {
  • return '00';
  • }
  • return 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 async function fetchColorData(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.tsx
App.css
  • import React, { useState, useRef, useEffect } from 'react';
  • // ...
  • interface Color {
  • image: string;
  • name: string;
  • }
  •  
  • function App(): JSX.Element {
  • // ...
  • const [visibleCards, setVisibleCards] = useState<Color[]>([]);
  • const colorsCache = useRef<Map<string, Color>>(new Map());
  •  
  • const fetchColorsForPage = useCallback(async (): Promise<void> => {
  • const startIndex = (pageIndex - 1) * pageSize;
  • const endIndex = startIndex + pageSize;
  • const hexSubset = hexCodes.current.slice(startIndex, endIndex);
  •  
  • const promises = hexSubset.map((hex) => {
  • if (colorsCache.current.has(hex)) {
  • return Promise.resolve(colorsCache.current.get(hex));
  • }
  • return fetchColorData(hex).then((color) => {
  • if (color) {
  • colorsCache.current.set(hex, color);
  • }
  • return color;
  • });
  • });
  •  
  • try {
  • const results = await Promise.all(promises);
  • const filteredColors = results.filter((color): color is Color => color !== null);
  • setVisibleCards(filteredColors);
  • } catch (error) {
  • console.error('Error fetching colors:', error);
  • }
  • }, [pageIndex, pageSize]);
  •  
  • useEffect(() => {
  • fetchColorsForPage().catch((error) => {
  • console.error('Error updating visible cards:', error);
  • });
  • }, [fetchColorsForPage]);
  •  
  • return (
  • <Pagination ... />
  • <div id="cards">
  • {visibleCards.map((color, index) => (
  • <div key={index}>
  • <img src={color.image} alt={color.name} />
  • </div>
  • ))}
  • </div>
  • );
  • }
  •  
  • export default App;
  • #cards {
  • display: flex;
  • justify-content: center;
  • flex-wrap: wrap;
  • }

Handle Page Size and Index Changes

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

App.tsx
  • import React, { useState, useRef, useEffect, useCallback } from 'react';
  • // ...
  •  
  • function App(): JSX.Element {
  • // ...
  • const onPageIndexChange = useCallback((value: number) => {
  • setPageIndex(value);
  • }, []);
  •  
  • const onPageSizeChange = useCallback((value: number) => {
  • setPageSize(value);
  • }, []);
  •  
  • return (
  • <Pagination ...
  • onPageIndexChange={onPageIndexChange}
  • onPageSizeChange={onPageSizeChange}
  • />
  • <!-- ... -->
  • );
  • }
  •  
  • export default App;

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.tsx
  • // ...
  • import LoadPanel, { Position } from 'devextreme-react/load-panel';
  •  
  • function App(): JSX.Element {
  • // ...
  • const [loadPanelVisible, setLoadPanelVisible] = useState<boolean>(false);
  •  
  • const fetchColorsForPage = useCallback(async (): Promise<void> => {
  • setLoadPanelVisible(true);
  • const startIndex = (pageIndex - 1) * pageSize;
  • const endIndex = startIndex + pageSize;
  • const hexSubset = hexCodes.current.slice(startIndex, endIndex);
  •  
  • const promises = hexSubset.map((hex) => {
  • // ...
  • });
  •  
  • try {
  • const results = await Promise.all(promises);
  • const filteredColors = results.filter((color): color is Color => color !== null);
  • setVisibleCards(filteredColors);
  • } catch (error) {
  • console.error('Error fetching colors:', error);
  • } finally {
  • setLoadPanelVisible(false);
  • }
  • }, [pageIndex, pageSize]);
  •  
  • // ...
  •  
  • return (
  • <Pagination ... />
  • <!-- ... -->
  • <LoadPanel
  • visible={loadPanelVisible}
  • showIndicator={true}
  • showPane={true}
  • hideOnOutsideClick={false}
  • >
  • <Position my="top" at="top" of="#cards" />
  • </LoadPanel>
  • );
  • }
  •  
  • export default App;