Server-side pagination in React JS + Material UI
Stephanie Opala
Posted on July 15, 2022
Server-side pagination involves making requests to the server to fetch subsets of data that match the query parameters of the request such as page size and page index. Unlike client-side pagination where we fetch all the data at once and handle the pagination on the frontend.
This article covers reasons why you should use server-side pagination and how to implement it in React JS and Material UI.
Table of contents
- Prerequisites
- Use Cases
- Benefits of server side pagination
- Getting Started
- Creating the table component
- Fetching paginated data
- Displaying data
- Handling pagination
- Conclusion
Prerequisites
To follow along, you will need to have:
- Basic knowledge of React JS.
Use Cases
Before we start building the application, below are some of the use cases where one would consider server-side pagination instead of client-side pagination.
- The data is large.
- The server supports server-side pagination.
Benefits of server-side pagination
- It is fast.
- Improved performance in your application especially when a large amount of data is involved.
Getting started
In the terminal, run the following command to create a React application using Create React App.
npx create-react-app my-app
Once the process is done, run the following command to get into the my-app folder.
cd my-app
Next, install the Material-UI library using the following command in your terminal. We will use the TablePagination
component from Material-UI when implementing pagination.
npm install @mui/material @emotion/react @emotion/styled
Start the React application using the following command.
npm start
In this article, we will use a free fake REST API from Instant Web Tools. However, if you have an existing API endpoint that supports server-side pagination, you can use that to follow along.
Creating the table component
In the src
folder, create a components
folder. Inside the components
folder, create a file Passengers.jsx
. This will contain the functional component that displays the list of passengers in a table.
import {
Card,
Table,
TableHead,
TableBody,
TableRow,
TableCell,
TablePagination
} from '@mui/material';
const Passengers = () => {
return (
<Card>
<Table>
<TableHead>
<TableRow>
<TableCell>
Name
</TableCell>
<TableCell>
Trips
</TableCell>
</TableRow>
</TableHead>
<TableBody>
</TableBody>
</Table>
<TablePagination />
</Card>
)
}
export default Users;
Fetching paginated data
In Passengers.jsx
, we will use the useEffect
hook to fetch data that will be displayed in the table.
import { useState, useEffect } from 'react';
import {
Card,
Table,
TableHead,
TableBody,
TableRow,
TableCell,
TablePagination
} from '@mui/material';
const Passengers = () => {
const [passengersList, setPassengersList] = useState([]);
const [passengersCount, setPassengersCount] = useState(0);
const [controller, setController] = useState({
page: 0,
rowsPerPage: 10
});
useEffect(() => {
const getData = async () => {
const url = `https://api.instantwebtools.net/v1/passenger?page=${controller.page}&size=${controller.rowsPerPage}`
try {
const response = await fetch(url);
if (response.statusText === 'OK') {
const data = await response.json();
console.log(data);
setPassengersList(data.data);
setPassengersCount(data.totalPassengers);
} else {
throw new Error('Request failed')
}
} catch (error) {
console.log(error);
}
};
getData();
}, [controller]);
return (
<Card>
<Table>
<TableHead>
<TableRow>
<TableCell>
Name
</TableCell>
<TableCell>
Trips
</TableCell>
</TableRow>
</TableHead>
<TableBody>
</TableBody>
</Table>
<TablePagination
/>
</Card>
)
}
export default Passengers;
In the code snippet above, we fetch data in the useEffect
hook which we import at the top of our file.
Inside the Passengers
functional component, we have three states that we are managing using the useState
hook.
const [passengersList, setPassengersList] = useState([]);
const [passengersCount, setPassengersCount] = useState(0);
const [controller, setController] = useState({
page: 0,
rowsPerPage: 10
});
passengersList
is an array that will contain the list of passengers that we will fetch from the API. passengersCount
is the value of the total number of passengers and controller
is an object with pagination information, that is, the page index and page size.
Inside the useEffect
hook, we have an asynchronous function, getData
that fetches the data when the page renders for the first and whenever the controller
state changes. We then have a try...catch
statement inside this function.
In the try
block of code, we make the API call using fetch
method and await
the response. We then check whether the request is successful or not. If successful, the response is converted to JSON format using the json()
method and stored in a variable data
. We then use the setPassengersList
setter function to update the state of the passengersList
. We do the same for setPassengersCount
which is updated with the total number of passengers from our API. On the other hand, if the request fails, an error is thrown.
In the catch
block of code, we console.log()
the error from the response to our console.
Displaying data
<Card>
<Table>
<TableHead>
<TableRow>
<TableCell>
Name
</TableCell>
<TableCell>
Trips
</TableCell>
</TableRow>
</TableHead>
<TableBody>
{passengersList.map((passenger) => (
<TableRow key={passenger._id}>
<TableCell>
{passenger.name}
</TableCell>
<TableCell>
{passenger.trips}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
<TablePagination />
</Card>
The above code snippet is the JSX that will be in the return statement. Since we have the data stored in the passengersList
state, we will map through each object and display the name and number of trips of the passenger in the table as shown above.
Handling pagination
<TablePagination
component="div"
onPageChange={handlePageChange}
page={controller.page}
count={passengersCount}
rowsPerPage={controller.rowsPerPage}
onRowsPerPageChange={handleChangeRowsPerPage}
/>
The TablePagination
component accepts the following props;
count
- This is the number of records/rows.
page
- This is the page index.
rowsPerPage
- This is the number of rows that will be displayed per page.
onPageChange
- This is a callback function that is fired when the page is changed.
onRowsPerPageChange
- This is the callback function that is called when the number of rows per page is changed.
component
- The component used for the root node.
In our case, we will pass these six props in our TablePagination
component. The first four props are required while the last two props are optional. This component also accepts several other props. More details can be found in the Material UI documentation.
Inside the Passengers
component just before we return the JSX, we will define two functions, handlePageChange
and handleChangeRowsPerPage
.
const handlePageChange = (event, newPage) => {
setController({
...controller,
page: newPage
});
};
const handleChangeRowsPerPage = (event) => {
setController({
...controller,
rowsPerPage: parseInt(event.target.value, 10),
page: 0
});
};
handlePageChange
will be called when the page is changed. Inside this function, we set the controller.page
state to the new page that we have navigated to. The useEffect
will run again and will cause a re-render.
handleChangeRowsPerPage
will be called when the number of rows per page is changed.
Below is the full code snippet in Passengers.jsx
.
import { useState, useEffect } from 'react';
import {
Card,
Table,
TableHead,
TableBody,
TableRow,
TableCell,
TablePagination
} from '@mui/material';
const Passengers = () => {
const [passengersList, setPassengersList] = useState([]);
const [passengersCount, setPassengersCount] = useState(0);
const [controller, setController] = useState({
page: 0,
rowsPerPage: 10
});
useEffect(() => {
const getData = async () => {
const url = `https://api.instantwebtools.net/v1/passenger?page=${controller.page}&size=${controller.rowsPerPage}`
try {
const response = await fetch(url);
if (response.statusText === 'OK') {
const data = await response.json();
console.log(data);
setPassengersList(data.data);
setPassengersCount(data.totalPassengers);
} else {
throw new Error('Request failed')
}
} catch (error) {
console.log(error);
}
};
getData();
}, [controller]);
const handlePageChange = (event, newPage) => {
setController({
...controller,
page: newPage
});
};
const handleChangeRowsPerPage = (event) => {
setController({
...controller,
rowsPerPage: parseInt(event.target.value, 10),
page: 0
});
};
return (
<Card>
<Table>
<TableHead>
<TableRow>
<TableCell>
Name
</TableCell>
<TableCell>
Trips
</TableCell>
</TableRow>
</TableHead>
<TableBody>
{passengersList.map((passenger) => (
<TableRow key={passenger._id}>
<TableCell>
{passenger.name}
</TableCell>
<TableCell>
{passenger.trips}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
<TablePagination
component="div"
onPageChange={handlePageChange}
page={controller.page}
count={passengersCount}
rowsPerPage={controller.rowsPerPage}
onRowsPerPageChange={handleChangeRowsPerPage}
/>
</Card>
)
}
export default Passengers;
Finally, import Passengers.jsx
file in App.js
.
On localhost
, below is the image of the final project.
Conclusion
This article has covered the use cases of server-side pagination, its benefits, and steps in implementing it in a React JS and Material UI application.
If you would like to know more about customizing your pagination and tables using Material UI, check out the official Material UI documentation.
Happy coding.
Posted on July 15, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.