In this tutorial, I will show you how to make React Table Pagination (Server side) with Search in a React Hooks Application using react-table v7 for data table and Material-UI for pagination.
Related Posts:
– React Table example: CRUD App | react-table 7
– React Hooks: JWT Authentication (without Redux) example
– React Hooks + Redux: JWT Authentication example
– React Hooks File Upload example with Axios & Progress Bar
Serverless with Firebase:
– React Hooks + Firebase Realtime Database: CRUD App
– React Hooks + Firestore example: CRUD app
Contents
- React Table Pagination (Server side) with Search example
- Server Side Pagination using Material-UI
- Technology
- Setup React Application
- Setup Material-UI for React Table Pagination App
- Initialize Axios for React HTTP Client
- Create Data Service
- Create React Table Pagination Component with Hooks
- React Material Table Pagination example
- Configure Port for Web API
- Run React Table Pagination with Search App
- Conclusion
- Further Reading
- Source Code
React Table Pagination (Server side) with Search example
One of the most important things to make a website friendly is the response time, and pagination comes for this reason. For example, this bezkoder.com website has hundreds of tutorials, and we don’t want to see all of them at once. Paging means displaying a small number of all, by a page.
Assume that we have tutorials table in database like this:
Our React.js app will display the result with react-table pagination (server side):
Change to a page with larger index:
We can change quantity of items per page (page size):
Or table pagination with search:
The API for this React client can be found at one of following posts:
– Node.js Express Pagination with MySQL
– Node.js Express Pagination with PostgreSQL
– Node.js Express Pagination with MongoDB
– Spring Boot Pagination & Filter example | Spring JPA, Pageable
– Spring Boot MongoDB Pagination example with Spring Data
These Servers will exports API for pagination (with/without filter), here are some url samples:
/api/tutorials?page=1&size=5
/api/tutorials?size=5
: using default value for page/api/tutorials?page=1
: using default value for size/api/tutorials?title=data&page=1&size=3
: pagination & filter by title containing ‘data’
This is structure of the response for the HTTP GET request:
{
"totalItems": 8,
"tutorials": [...],
"totalPages": 3,
"currentPage": 1
}
We actually only need to use tutorials
and totalPages
when working with Material-UI.
Server Side Pagination using Material-UI
Material-UI provides Pagination component that enables the user to select a specific page from a range of pages.
For example, with Pagination
components below:
<Pagination count={10} />
<Pagination count={10} color="primary" />
<Pagination count={10} variant="outlined" />
<Pagination count={10} variant="outlined" color="primary" />
<Pagination count={10} shape="rounded" />
<Pagination count={10} variant="outlined" shape="rounded" />
We’re gonna have UI like this-
There are also 2 optional props for Ranges:
siblingRangespecify
: how many digits to display either side of current pageboundaryRange
: adjacent to the start and end page number
For example:
<Pagination count={11} defaultPage={6} siblingCount={0} />
<Pagination count={11} defaultPage={6} /> {/* Default ranges */}
<Pagination count={11} defaultPage={6} siblingCount={0} boundaryCount={2} />
<Pagination count={11} defaultPage={6} boundaryCount={2} />
For handling page changes, we use onChange
:
export default function PaginationControlled() {
const [page, setPage] = React.useState(1);
const handleChange = (event, value) => {
setPage(value);
};
return (
<div>
<Pagination count={10} page={page} onChange={handleChange} />
</div>
);
}
Notice that count
is totalPages
in the API response, and page
is the current page.
Technology
- React 17/16
- axios 0.21.1
- react-table 7.6.3
- bootstrap 4
- material-ui 4
Setup React Application
Open cmd at the folder you want to save Project folder, run command:
npx create-react-app react-table-pagination-server-side
After the process is done. We create additional folders and files like the following tree:
public
src
components
AddTutorial.js
Tutorial.js
TutorialsList.js
services
TutorialService.js
App.css
App.js
index.js
package.json
You can follow step by step, or get source code in this post:
React Table example: CRUD App | react-table 7
The React Project contains structure that we only need to add some changes (in TutorialsList.js and TutorialService.js) to make the pagination work well.
Or you can get the new Github source code at the end of this tutorial.
Setup Material-UI for React Table Pagination App
We need to install both Material-UI core and lab with command:
npm install @material-ui/core @material-ui/lab
Initialize Axios for React HTTP Client
Let’s install axios with command: npm install axios
.
Under src folder, we create http-common.js file with following code:
import axios from "axios";
export default axios.create({
baseURL: "http://localhost:8080/api",
headers: {
"Content-type": "application/json"
}
});
For more details about ways to use Axios, please visit:
Axios request: Get/Post/Put/Delete example
Create Data Service
In this step, we’re gonna create a service that uses axios
object above to send HTTP requests.
services/TutorialService.js
import http from "../http-common";
const getAll = (params) => {
return http.get("/tutorials", { params });
};
// other CRUD methods
export default {
getAll,
...
};
In the code above, you can see that we pass params
object to GET method.
The params
object will have one, two or all of the fields: title
, page
, size
.
Create React Table Pagination Component with Hooks
This component has:
- a search bar for finding Tutorials by title
- a select element for quantity of items per page
- a Material-UI Pagination component
- a tutorials array displayed as a table
So we will have following state:
– search and display Tutorials:
searchTitle
tutorials
– pagination:
page
: current pagecount
: total pagespageSize
: number of items in each page
For pagination, we need to use TutorialDataService.getAll()
methods.
components/TutorialsList.js
import React, { useState, useEffect } from "react";
import TutorialDataService from "../services/TutorialService";
...
const TutorialsList = () => {
const [tutorials, setTutorials] = useState([]);
const [searchTitle, setSearchTitle] = useState("");
const [page, setPage] = useState(1);
const [count, setCount] = useState(0);
const [pageSize, setPageSize] = useState(3);
const pageSizes = [3, 6, 9];
const onChangeSearchTitle = (e) => {
const searchTitle = e.target.value;
setSearchTitle(searchTitle);
};
const getRequestParams = (searchTitle, page, pageSize) => {
let params = {};
if (searchTitle) {
params["title"] = searchTitle;
}
if (page) {
params["page"] = page - 1;
}
if (pageSize) {
params["size"] = pageSize;
}
return params;
};
const retrieveTutorials = () => {
const params = getRequestParams(searchTitle, page, pageSize);
TutorialDataService.getAll(params)
.then((response) => {
const { tutorials, totalPages } = response.data;
setTutorials(tutorials);
setCount(totalPages);
console.log(response.data);
})
.catch((e) => {
console.log(e);
});
};
useEffect(retrieveTutorials, [page, pageSize]);
...
const findByTitle = () => {
setPage(1);
retrieveTutorials();
};
const handlePageChange = (event, value) => {
setPage(value);
};
const handlePageSizeChange = (event) => {
setPageSize(event.target.value);
setPage(1);
};
return (
...
);
};
export default TutorialsList;
Let me explain some lines of code.
In the retrieveTutorials()
method:
– We get searchTitle
, page
, pageSize
state and transform them into params
object:
{
"title": searchTitle,
"page": page - 1,
"size": pageSize
}
– We use tutorials
and totalPages
as count
state from the response data:
{
"totalItems": 8,
"tutorials": [...],
"totalPages": 3,
"currentPage": 1
}
handlePageChange()
and handlePageSizeChange()
methods are for setting new page
and pageSize
with callback invoking retrieveTutorials()
method that updates the tutorials List when pagination information changes.
You can simplify import statement with:
Absolute Import in React
Let’s continue to implement the UI.
React Material Table Pagination example
This is where we use react-table
for displaying tabular data. In addition to tutorial’s fields (title, description, status), we also have Actions column with edit & delete icon buttons.
Install react-table
with command: npm install react-table
.
Similar to any table, a React Table includes columns
and data
:
columns
: array of columns which act as header groups. The columns can be recursively nested as much as needed.data
: array of rows to be displayed on the table.
const columns = [
{
Header: "Title",
accessor: "title",
},
{
Header: "Description",
accessor: "description",
},
...
];
const data = [
{
title: "bezkoder Tut#1",
description: "description Tut#1",
},
{
title: "bezkoder Tut#2",
description: "description Tut#2",
},
...
];
Now, how to modify the value of the cell, for example:
- if cell value is
true
/false
, change it to'Published'
/'Pending'
- set a custom html element with icons and button click events
react-table
supports the way to work with Cell property like this:
const columns = [
...,
{
Header: "Status",
accessor: "published",
Cell: (props) => {
return props.value ? "Published" : "Pending";
},
},
{
Header: "Actions",
accessor: "actions",
Cell: (props) => {
const rowIdx = props.row.id;
return (
<div>
<span onClick={() => openTutorial(rowIdx)}>
<i className="far fa-edit action mr-2"></i>
</span>
<span onClick={() => deleteTutorial(rowIdx)}>
<i className="fas fa-trash action"></i>
</span>
</div>
);
},
}
];
const data = [
{
title: "bezkoder Tut#1",
description: "description Tut#1",
published: true
},
{
title: "bezkoder Tut#2",
description: "description Tut#2",
published: false
},
...
];
If you click on edit icon button of any Tutorial, the app will call openTutorial()
method and direct you to Tutorial page. Similar for trash
icon button, deleteTutorial()
will be invoked.
Now we’re gonna use a custom hook that react-table provides – useTable()
– which implements many features: row sorting, filtering, searching, pagination, row selection, infinity scrolling…
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
prepareRow,
} = useTable({
columns,
data: tutorials,
});
useTable()
takes columns
and data
to build a table instance.
We use several props from the instance:
getTableProps()
function is called inside thetable
tags to resolve any props needed by the table wrapper which built-in props is{role: "table"}
.getTableBodyProps()
function is called inside thetbody
tags resolves any props needed by the table body wrapper which built-in props is {role: “rowgroup”}.prepareRow()
function must be called on any rows to be displayed. It is responsible for lazily preparing a row for rendering.headerGroups
androws
are internal data structures derived fromcolumns
anddata
above.
This is full code for building the UI and table:
...
import React, { useState, useEffect, useMemo, useRef } from "react";
import Pagination from "@material-ui/lab/Pagination";
import { useTable } from "react-table";
const TutorialsList = (props) => {
...
const columns = useMemo(
() => [
{
Header: "Title",
accessor: "title",
},
{
Header: "Description",
accessor: "description",
},
{
Header: "Status",
accessor: "published",
Cell: (props) => {
return props.value ? "Published" : "Pending";
},
},
{
Header: "Actions",
accessor: "actions",
Cell: (props) => {
const rowIdx = props.row.id;
return (
openTutorial(rowIdx)}>
deleteTutorial(rowIdx)}>
);
},
},
],
[]
);
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
prepareRow,
} = useTable({
columns,
data: tutorials,
});
return (
<div className="list row">
<div className="col-md-8">
<div className="input-group mb-3">
<input
type="text"
className="form-control"
placeholder="Search by title"
value={searchTitle}
onChange={onChangeSearchTitle}
/>
<div className="input-group-append">
<button
className="btn btn-outline-secondary"
type="button"
onClick={findByTitle}
>
Search
</button>
</div>
</div>
</div>
<div className="col-md-12 list">
<div className="mt-3">
{"Items per Page: "}
<select onChange={handlePageSizeChange} value={pageSize}>
{pageSizes.map((size) => (
<option key={size} value={size}>
{size}
</option>
))}
</select>
<Pagination
className="my-3"
count={count}
page={page}
siblingCount={1}
boundaryCount={1}
variant="outlined"
shape="rounded"
onChange={handlePageChange}
/>
</div>
<table
className="table table-striped table-bordered"
{...getTableProps()}
>
<thead>
{headerGroups.map((headerGroup) => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map((column) => (
<th {...column.getHeaderProps()}>
{column.render("Header")}
</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{rows.map((row, i) => {
prepareRow(row);
return (
<tr {...row.getRowProps()}>
{row.cells.map((cell) => {
return (
<td {...cell.getCellProps()}>{cell.render("Cell")}</td>
);
})}
</tr>
);
})}
</tbody>
</table>
</div>
<div className="col-md-8">
<button className="btn btn-sm btn-danger" onClick={removeAllTutorials}>
Remove All
</button>
</div>
</div>
);
};
export default TutorialsList;
Configure Port for Web API
Because most of HTTP Server use CORS configuration that accepts resource sharing retrictted to some sites or ports, so we also need to configure port for our App.
In project folder, create .env file with following content:
PORT=8081
Now we’ve set our app running at port 8081
.
Run React Table Pagination with Search App
First you need to run the Server at one of following posts:
- Node.js Express Pagination with MySQL
- Node.js Express Pagination with PostgreSQL
- Node.js Express Pagination with MongoDB
- Spring Boot Pagination & Filter example | Spring JPA, Pageable
- Spring Boot MongoDB Pagination example with Spring Data
Then you can run our App with command: npm start
.
If the process is successful, open Browser with Url: http://localhost:8081/
and check it.
Conclusion
Today we’ve built a React Table Server Side Pagination app with Search that consumes API successfully using react-table v7, Bootstrap and Material UI. I hope you apply it in your project at ease.
If you want to know more details about CRUD operations in the source code, please visit:
React Table example: CRUD App | react-table 7
Happy learning, see you again!
Further Reading
- https://material-ui.com/components/pagination/
- https://www.npmjs.com/package/@material-ui/core
- https://www.npmjs.com/package/@material-ui/lab
For more details about ways to use Axios, please visit:
Axios request: Get/Post/Put/Delete example
Related Posts:
– React Hooks CRUD example with Axios and Web API
– React Hooks: JWT Authentication (without Redux) example
– React Hooks + Redux: JWT Authentication example
– React Hooks File Upload example with Axios & Progress Bar
Serverless with Firebase:
– React Hooks + Firebase Realtime Database: CRUD App
– React Hooks + Firestore example: CRUD app
Source Code
You can find the complete source code for this tutorial on Github.
This worked very well for me. Thanks for taking the time to code and document.