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;
}