Pagination refers to dividing large datasets into smaller pages, enhancing performance and navigation.
Key takeaways:
Pagination reduces loading time and improves user experience by dividing large datasets into smaller, manageable pages.
React’s functional components with Hooks like useState
and useEffect
simplify pagination management. These Hooks allow developers to manage the page state and perform data fetching when components mount.
Developers can use axios
or fetch
to retrieve data from APIs or local JSON files.
Server-side pagination is recommended for large datasets. It minimizes data transfer between the client and server, improving performance, especially when dealing with APIs that return large datasets.
Client-side pagination is a common method used in web development—on the client side—to divide large data sets into smaller, more manageable chunks. This approach ensures that the entire dataset is loaded on the client side, with only a subset displayed at any given time. Client-side pagination functionality can be easily and effectively implemented using React, a well-known JavaScript library for creating user interfaces.
We’ll learn to fetch and display data in a paginated format. Add pagination controls to navigate through pages and also manage state to keep track of the current page and handle user interactions.
We’ll explore how to implement pagination in a React application by following a few steps, using a simple example. All of our components, as well as the logic for obtaining the data and segmenting it for pagination, will be stored in App.jsx
file.
import React from 'react' // Importing the React libraryfunction App() { // Defining the App componentreturn (<div></div>);}export default App; // Exporting the App component as the default export
Now, add the following two states to the App
component.
const [data, setData] = useState([]) // Initialize 'data' state with an empty array []const [loading, setLoading] = useState(true); // Initialize 'loading' state with the value 'true', indicating that data is still loading
The data
state will store the actual data fetched using Axios.
The loading
state indicates whether the data has been fetched or not.
Axios is a JavaScript library often used to make HTTP calls. Its API is easy to use and supports modern browsers.
First, to set up Axios, run the following command in the root directory of your project.
npm install axios
Next, import Axios at the top of the component file where you want to make the request to fetch data.
import axios from 'axios';
Then, create a JSON file named data.json
with the following sample data:
[{"id": 1,"first_name": "Humfrid","last_name": "Larwood","city": "Tatebayashi"},{"id": 2,"first_name": "Darlleen","last_name": "Peegrem","city": "Tianning"},{"id": 3,"first_name": "Alana","last_name": "Francisco","city": "Orléans"},{"id": 4,"first_name": "Glyn","last_name": "Littledike","city": "Rio Real"},{"id": 5,"first_name": "Camile","last_name": "Cano","city": "Tampa"},{"id": 6,"first_name": "Ara","last_name": "Oneil","city": "Calçada"},{"id": 7,"first_name": "Kimberlyn","last_name": "Gilfoy","city": "Suresnes"},{"id": 8,"first_name": "Magdalene","last_name": "Kunzel","city": "Kalloní"},{"id": 9,"first_name": "Rosalyn","last_name": "Dobsons","city": "Nakło nad Notecią"},{"id": 10,"first_name": "Kiley","last_name": "Rimes","city": "Nanxi"},{"id": 11,"first_name": "Lenette","last_name": "Brokenshaw","city": "Lộc Bình"},{"id": 12,"first_name": "Julie","last_name": "Origin","city": "Yanshou"},{"id": 13,"first_name": "Carr","last_name": "Haug","city": "Fujikawaguchiko"},{"id": 14,"first_name": "Duke","last_name": "Kuhnel","city": "Nyima"},{"id": 15,"first_name": "Yulma","last_name": "Stanwix","city": "Río Alejandro"},{"id": 16,"first_name": "Mei","last_name": "Keith","city": "Tucson"},{"id": 17,"first_name": "Scotty","last_name": "Suggitt","city": "Sukamenak"},{"id": 18,"first_name": "Mirelle","last_name": "Howbrook","city": "Viçosa do Ceará"},{"id": 19,"first_name": "Debor","last_name": "Foad","city": "Kendayakan"},{"id": 20,"first_name": "Cross","last_name": "Karppi","city": "Mozdok"},{"id": 21,"first_name": "Byrle","last_name": "Byles","city": "Rybno"},{"id": 22,"first_name": "York","last_name": "Grime","city": "Shaxi"},{"id": 23,"first_name": "Findlay","last_name": "Vettore","city": "Радовиш"},{"id": 24,"first_name": "Consolata","last_name": "Selwin","city": "Ban Houayxay"},{"id": 25,"first_name": "Shannah","last_name": "Spivie","city": "Şuraabad"},{"id": 26,"first_name": "Tyrone","last_name": "Westnedge","city": "Lendan"},{"id": 27,"first_name": "Glen","last_name": "Kaysor","city": "Jincheng"},{"id": 28,"first_name": "Malissia","last_name": "Alfwy","city": "Cartagena"},{"id": 29,"first_name": "Alice","last_name": "Ondrousek","city": "Potrero Grande"},{"id": 30,"first_name": "Rossy","last_name": "Mosley","city": "Daşoguz"},{"id": 31,"first_name": "Tybi","last_name": "Scoines","city": "Paris 12"},{"id": 32,"first_name": "Jerrome","last_name": "Setchfield","city": "Kalidawir"},{"id": 33,"first_name": "Ginelle","last_name": "Ickovicz","city": "Wamba"},{"id": 34,"first_name": "Bendicty","last_name": "Morigan","city": "Malinta"},{"id": 35,"first_name": "Susie","last_name": "Allwright","city": "Penisihan"},{"id": 36,"first_name": "Bonnie","last_name": "Hullin","city": "Hradec Králové"},{"id": 37,"first_name": "Lawry","last_name": "Kynsey","city": "Podporozh’ye"},{"id": 38,"first_name": "Kakalina","last_name": "Thornborrow","city": "Plalar"},{"id": 39,"first_name": "Floria","last_name": "Chaudret","city": "Lanlongkou"},{"id": 40,"first_name": "Alexa","last_name": "Flaubert","city": "Borlänge"},{"id": 41,"first_name": "Rhodia","last_name": "Dacks","city": "Rurrenabaque"},{"id": 42,"first_name": "Ignace","last_name": "Lortzing","city": "Gandusari"},{"id": 43,"first_name": "Trude","last_name": "Trobey","city": "Tuchengzi"},{"id": 44,"first_name": "Dahlia","last_name": "Mompesson","city": "Sarreguemines"},{"id": 45,"first_name": "Lynnet","last_name": "Densham","city": "Yangzi"},{"id": 46,"first_name": "Alley","last_name": "Coetzee","city": "Ulyanovsk"},{"id": 47,"first_name": "Loren","last_name": "Fouracre","city": "Shahbā"},{"id": 48,"first_name": "Lamont","last_name": "Boutflour","city": "Bāndarban"},{"id": 49,"first_name": "Norby","last_name": "O'Cuddie","city": "Libas"},{"id": 50,"first_name": "Devon","last_name": "Edson","city": "Chelyabinsk"},{"id": 51,"first_name": "Francoise","last_name": "Bevan","city": "Calapan"},{"id": 52,"first_name": "Lettie","last_name": "Demcik","city": "Kresna"},{"id": 53,"first_name": "Camila","last_name": "Capin","city": "Moutsamoudou"},{"id": 54,"first_name": "Cammy","last_name": "Feldberg","city": "Guang’an"},{"id": 55,"first_name": "Juliette","last_name": "Braunstein","city": "Yucheng"},{"id": 56,"first_name": "Norman","last_name": "Spread","city": "Angol"},{"id": 57,"first_name": "Sheela","last_name": "Sellstrom","city": "Hongtu"},{"id": 58,"first_name": "Angelia","last_name": "Desbrow","city": "Santa Elena"},{"id": 59,"first_name": "Frederica","last_name": "Fulleylove","city": "Hengli"},{"id": 60,"first_name": "Seana","last_name": "Leither","city": "Samparna"},{"id": 61,"first_name": "Aleen","last_name": "Springtorpe","city": "Xiaoweizhai"},{"id": 62,"first_name": "Desiri","last_name": "De Bernardi","city": "San Diego"},{"id": 63,"first_name": "Karim","last_name": "Bloor","city": "Hövsan"},{"id": 64,"first_name": "Jennifer","last_name": "Hallgate","city": "Wantou"},{"id": 65,"first_name": "Dawn","last_name": "Soars","city": "Wilkołaz"},{"id": 66,"first_name": "Thomasa","last_name": "Beardsworth","city": "Norrköping"},{"id": 67,"first_name": "Brennen","last_name": "Brenton","city": "Yongshan"},{"id": 68,"first_name": "Wini","last_name": "Ackerley","city": "Korolevo"},{"id": 69,"first_name": "Mufinella","last_name": "Nevins","city": "Qimantage"},{"id": 70,"first_name": "Pammi","last_name": "Dollin","city": "Glad"},{"id": 71,"first_name": "Royce","last_name": "Friedlos","city": "Konso"},{"id": 72,"first_name": "Xever","last_name": "Fanthom","city": "Monte"},{"id": 73,"first_name": "Becki","last_name": "Kiddie","city": "Skópelos"},{"id": 74,"first_name": "Joscelin","last_name": "Holleworth","city": "Jataí"},{"id": 75,"first_name": "Skipper","last_name": "Crepin","city": "Alasmalang"},{"id": 76,"first_name": "Nelle","last_name": "Lazenby","city": "Koronowo"},{"id": 77,"first_name": "Babbette","last_name": "Bickerstaffe","city": "Potrero Grande"},{"id": 78,"first_name": "Ricca","last_name": "Carcas","city": "Bhairab Bāzār"},{"id": 79,"first_name": "Zarla","last_name": "Po","city": "Le Port"},{"id": 80,"first_name": "Mil","last_name": "Lowers","city": "Telangi Satu"},{"id": 81,"first_name": "Douglas","last_name": "Pee","city": "Itsukaichi"},{"id": 82,"first_name": "Eden","last_name": "Mirando","city": "São Paio de Gramaços"},{"id": 83,"first_name": "Nefen","last_name": "Achrameev","city": "Yuqunweng"},{"id": 84,"first_name": "Erroll","last_name": "Melmore","city": "Qingxi"},{"id": 85,"first_name": "Dehlia","last_name": "Hinkensen","city": "Chichibu"},{"id": 86,"first_name": "Mathilde","last_name": "Kleimt","city": "Tarbes"},{"id": 87,"first_name": "Henka","last_name": "O'Sherrin","city": "Huatan"},{"id": 88,"first_name": "Dieter","last_name": "Hanne","city": "Kanbe"},{"id": 89,"first_name": "Bentlee","last_name": "Pooley","city": "Tauca"},{"id": 90,"first_name": "Emogene","last_name": "Henstridge","city": "Saint-Amand-les-Eaux"},{"id": 91,"first_name": "Thatcher","last_name": "Poyle","city": "Gobernador Ingeniero Valentín Virasoro"},{"id": 92,"first_name": "Heindrick","last_name": "Tondeur","city": "Pecatu"},{"id": 93,"first_name": "Gal","last_name": "Dyster","city": "Al Bājūr"},{"id": 94,"first_name": "Tova","last_name": "Alcock","city": "Yantal’"},{"id": 95,"first_name": "Sosanna","last_name": "Rief","city": "Cagwait"},{"id": 96,"first_name": "Mirna","last_name": "Brooksbie","city": "Dayuanhuizu"},{"id": 97,"first_name": "Ethelin","last_name": "Buckner","city": "Wellington"},{"id": 98,"first_name": "Gael","last_name": "Poulsum","city": "Ryczywół"},{"id": 99,"first_name": "Libby","last_name": "Harbottle","city": "Leskovac"},{"id": 100,"first_name": "Leda","last_name": "Bugler","city": "Pak Phanang"}]
Fetch this data inside the useEffect
Hook using the get
method. It accepts a URL as an input and performs a get request on it.
// useEffect Hook to fetch data from 'data.json' using axios when the component mountsuseEffect(() => {axios.get('data.json').then(res => {setData(res.data);setLoading(false);}).catch(() => { // Error handling: Displays an alert if there's an error fetching dataalert('There was an error while retrieving the data')})}, [])
Line 2: Use the useEffect
Hook to ensure that the data fetching process is initiated when the component loads.
Lines 4–7: After receiving the full response, perform the subsequent actions inside the then
block of the axios.get()
function.
Line 5: Update the data
state with the received data from the response.
Line 6: Set the loading
state to false
to indicate that the data fetching process is complete.
Lines 8–10: Implement the catch
block to handle errors that may occur during the data fetching process.
Want to get a hands-on experice with Axios? Try this project: Build an Online Video Player in React and YouTube Data API.
Create a component where you want to display the fetched data. In this example, we will display our data in the form of a table in a Records
component.
Line 1: The data
prop is used to populate the table rows dynamically based on the provided data.
Line 13: The data.map()
function is used to iterate over each item in the data
array.
Lines 14–19: For each item, a new table row (<tr>
) is created, and the relevant data fields (ID, first name, last name, city) are displayed in separate table cells (<td>
).
So far, the table displays all records on a single page, which can be overwhelming for users. To address this issue, pagination has been introduced as a solution. We will be working on the App
component.
We have the flexibility to determine the number of records to be displayed on each page. This choice influences the calculation of the total number of pages. Additionally, tracking the current page number that the user is viewing is crucial.
const [currentPage, setCurrentPage] = useState(1);const [recordsPerPage] = useState(5);
To determine the range of records to be displayed on the current page, we can calculate the index of the last record and the index of the first record. The index of the last record is obtained by multiplying the current page number, currentPage
, by the number of records per page, recordsPerPage
.
Similarly, the index of the first record can be obtained by subtracting the number of records per page, recordsPerPage
, from the index of the last record, endIndex
.
const endIndex = currentPage * recordsPerPage;const indexOfFirstRecord = endIndex - recordsPerPage;
Get hands-on practice with React with a real-world application with this project: Build the Frontend of a Financial Application Using React.
To display the records on the current page, we can use the slice
function in JavaScript. By specifying the range of indexes using indexOfFirstRecord
and endIndex
, we can extract the subset of records from the data
array.
Note: The
slice
function's end index is exclusive, so the range fromendIndex
toendIndex - 1
will be selected.
// Records to be displayed on the current pageconst currentRecords = data.slice(indexOfFirstRecord, endIndex);
The resulting subset of records is then passed as props to the Records
component, which will render the table with the records specific to the current page.
To calculate the number of pages needed to display all the records, we can use the Math.ceil
function in JavaScript. By dividing the total number of records, data.length
, by the desired number of records per page, recordsPerPage
, we will obtain the quotient.
If any remaining records cannot fit completely on a page, the Math.ceil
function ensures that an additional page is allocated to accommodate them. The resulting value is assigned to the nPages
variable, representing the total number of pages in the pagination.
const nPages = Math.ceil(data.length / recordsPerPage)
Pagination
componentTo implement pagination, create a new component called Pagination
. The necessary variables, nPages
, currentPage
, and setCurrentPage
, are passed to the pagination component as props.
function Pagination({ nPages, currentPage, setCurrentPage }) {return (<div>Pagination Component</div>)}
Within the pagination component, generate an array containing all the page numbers from 1
to nPages
. This can be accomplished by utilizing the spread operator (...
) along with the Array
and keys()
methods. To exclude the first element, 0
, from the array, use the slice
method.
const pageNumbers = [...Array(nPages + 1).keys()].slice(1);
The pageNumbers
array will be a reference for rendering the pagination buttons or links, allowing users to navigate between pages.
Next, create a container to display the page numbers and page controls. This container will hold the pagination buttons or links that allow users to navigate between pages.
When a page number is clicked, it triggers an event that sets the current page to that specific number. As a result, the table dynamically displays the records corresponding to the selected page.
To enable navigation between pages, implement the goToPrevPage
and goToNextPage
functions.
goToPrevPage
functionCreate the goToPrevPage
function to decrease the current page number by 1
.
Add a condition to check if the current page is not the first page, currentPage !== 1
.
If the condition is met, update the current page by calling setCurrentPage
with the decremented value, setCurrentPage(currentPage - 1)
.
const goToPrevPage = () => {if (currentPage !== 1) setCurrentPage(currentPage - 1);};
goToNextPage
functionCreate the goToNextPage
function to increase the current page number by 1
.
Add a condition to check if the current page is not the last page, currentPage !== nPages
.
If the condition is met, update the current page by calling setCurrentPage
with the incremented value setCurrentPage(currentPage + 1)
.
const goToNextPage = () => {if (currentPage !== nPages) setCurrentPage(currentPage + 1);};
These functions ensure that the current page is within the valid range and update the state accordingly to reflect the new page.
Let’s look at a working example by combining everything and executing the application.
import React, { useState, useEffect } from 'react' import axios from 'axios' import Records from './components/Records'; import Pagination from './components/Pagination'; function App() { const [data, setData] = useState([]) const [loading, setLoading] = useState(true); const [currentPage, setCurrentPage] = useState(1); const [recordsPerPage] = useState(10); useEffect(() => { axios.get('data.json') .then(res => { setData(res.data); setLoading(false); }) .catch(() => { alert('There was an error while retrieving the data') }) }, []) const endIndex = currentPage * recordsPerPage; const indexOfFirstRecord = endIndex - recordsPerPage; const currentRecords = data.slice(indexOfFirstRecord, endIndex); const nPages = Math.ceil(data.length / recordsPerPage) return ( <div className='container mt-5'> <h2> Simple Pagination Example in React </h2> <Records data={currentRecords}/> <Pagination nPages={nPages} currentPage={currentPage} setCurrentPage={setCurrentPage} /> </div> ); } export default App;
We successfully implemented pagination in a React application. We utilized React hooks, such as useState
and useEffect
, to manage the state and handle asynchronous data fetching. By dividing the data into smaller chunks and displaying a limited number of records on each page, we greatly improved the user experience and performance of the application.
Explore these projects for hands-on practice for creating real-world applications with React:
Haven’t found what you were looking for? Contact Us
Free Resources