Pagination

Build a component that shows numbered pages and allows users to click through pages of data (data can be static).

Solution

  • ID: 1 - John Tar

    Mechanic

  • ID: 2 - Alice Johnson

    Software Engineer

  • ID: 3 - Sam Bridge

    Teacher

Explanation

The HTML Structure

Let's look at displaying the data and outputting the buttons.

The Content

The content is a list so to follow good semantic HTML practice it uses a list container.

To render the data to the page, map over the data array and return li elements.

The pagination buttons

The number of page buttons is dependant on the amount of data so you will also map these out. The page number is passed down to the element. Styling is conditional on whether it is the current page.

Handling the page numbers

The pages needed is calculated by dividing the number of items by the number you want on each page. Three has been chosen for this example.

Make an array with the number of pages then cycle through the values adding numbers starting from 1.

Showing the data for that page

This is calculated at pageList. Take the page you are on (zero-indexed) and go up to the next page. Multiply that calculation by the number of items per page.

Handling the page button function

Each page button has the onClick function, handlePageChange attached. Note how it is written, () => handlePageChange(page). This allows you to pass the page number back to the function to work with and apply the function to all button elements without having to hard-code the page values.

Keeping track of the page number

The simplest part of the setup is keeping track of the current page with useState, for the value, currentPage.

This leaves the function, handlePageChange with only one job to do, setCurrentPage.

Bonus notes

Check out the aria-label={`Go to page {page}`} on the pagination button. It's a small edit that makes the button useful for screen readers.

Code

import { useState } from "react";
import styles from "./Solution.module.css";

const dataArray = [
  { id: 1, name: "John Tar", JobTitle: "Mechanic" },
  { id: 2, name: "Alice Johnson", JobTitle: "Software Engineer" },
  { id: 3, name: "Sam Bridge", JobTitle: "Teacher" },
  { id: 4, name: "Lila Stone", JobTitle: "Graphic Designer" },
  { id: 5, name: "Raj Patel", JobTitle: "Accountant" },
  { id: 6, name: "Chris Lee", JobTitle: "Chef" },
  { id: 7, name: "Mia Wong", JobTitle: "Architect" },
  { id: 8, name: "Tom Hanks", JobTitle: "Actor" },
  { id: 9, name: "Sofia Cruz", JobTitle: "Nurse" },
];

const Solution = () => {
  const [currentPage, setCurrentPage] = useState(1);
  const pageSize = 3;
  const totalPages = Math.ceil(dataArray.length / pageSize);

  // create an array with the number of pages
  const pages = new Array(totalPages).fill(null).map((_, index) => index + 1);

  // range to show
  const pageList = dataArray.slice(
    (currentPage - 1) * pageSize,
    currentPage * pageSize
  );

  const handlePageChange = (page: number) => {
    setCurrentPage(page);
  };

  return (
    <div>
      <ul>
        {pageList.map((person) => (
          <li className={styles.listItem} key={person.id}>
            <h3>
              ID: {person.id} - {person.name}
            </h3>
            <p>{person.JobTitle}</p>
          </li>
        ))}
      </ul>
      <div className={styles.buttonContainer}>
        {pages.map((page) => (
          <button
            key={page}
            onClick={() => handlePageChange(page)}
            style={{
              backgroundColor: page === currentPage ? "#7B8BDE" : "transparent",
            }}
            className={styles.button}
            aria-label={`Go to page ${page}`}
          >
            {page}
          </button>
        ))}
      </div>
    </div>
  );
};

export default Solution;

Styling

.button {
  border: 1px solid black;
  border-radius: 5px;
  padding: 5px;
  cursor: pointer;
}

.buttonContainer {
  display: flex;
  justify-content: flex-start;
  margin-top: 20px;
  gap: 5px;
}

.listItem {
  margin-bottom: 10px;
}

Links