Pagination is an important feature on websites because it makes things easier for users. Instead of overwhelming them with a lot of information on one page, it breaks the content into smaller parts. This helps the page load faster and allows users to navigate through different sections easily. so in this article we will implement our custom datatable in react js with pagination and searching by using react hooks only.
Pagination also makes websites more accessible for people with disabilities. It’s like a table of contents that helps users find what they need without getting lost. Overall, pagination improves the experience of browsing websites, especially on mobile devices, and helps search engines find and rank the website better.
Access All Code here : https://github.com/SiteJuggler/custom-datatables-in-react-using-hooks
Create React Project For Implementing Datatable
Install node js environment
To create a new React project, you’ll need to install Node.js on your computer.
Node.js is a special tool that allows developers to use JavaScript outside of web browsers. When working with React, Node.js helps set up a server to run and test the React application while it’s being developed. It also provides useful tools like npm, which helps manage the pieces of code needed for the React app to work correctly. When it’s time to make the React app live on the internet, Node.js is used to host and run the code that works behind the scenes, handling things like sending and receiving data from the user. In summary, Node.js is a helpful tool that makes it easier to build and run React apps both during development and when they’re live on the web.
to install node js on your computer please check official documentation https://nodejs.org/
Create React Project Using create-react-app
Go to the folder on your computer where you want to work and open the terminal or command line interface (CLI) tool in that folder. The terminal or CLI is a program that allows you to type commands and perform actions on your computer. By opening it in the folder you want to work in, you’ll be able to execute commands specific to that folder and easily manage your files or projects.
In the terminal, run the following command to install create-react-app
globally on your system:
npm install -g create-react-app
To generate the basic structure needed for a React project, you can run the following command. This command sets up the initial files and folders required to start working on your React project.
npx create-react-app react-datatables


Once you have successfully executed the previous command, go to the “react-datatables” folder and run the command “npm start” to start the development server on http://localhost:3000. This command will launch the server and allow you to begin working on your project, making it visible in your web browser for testing and development purposes.


Once you have successfully started the development server for your React app, it will automatically open a web browser and display your app at “localhost:3000”. If the browser doesn’t open automatically, you can manually open any web browser like Google Chrome and type “http://localhost:3000” in the address bar. This will load and show your React app in the browser, allowing you to see and interact with it during the development process.
Open project in Visual studio code or Any Code editor
To resume working on your React project, launch Visual Studio Code. Click on the “File” menu and choose “Open”. Select the folder where you previously created your React project. This action will open the project in Visual Studio Code, enabling you to edit and modify your React code within the editor.


Add Bootstrap CDN to your Project
In this project, we utilize Bootstrap 5 for styling purposes, allowing us to prioritize the logical aspects of development rather than spending excessive time on writing CSS code. By using Bootstrap, we can leverage its pre-designed styles and components to create a visually pleasing and professional-looking website more efficiently.
To include Bootstrap styles and classes in your project, follow these steps:
- Go to the “public” folder.
- Open the “index.html” file.
- In the header section of the HTML file, paste the following link tag. This link tag connects your project to a Content Delivery Network (CDN) that provides Bootstrap’s styles and classes, making them available for use in your project.
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">


Create Simple Datatable in App.js
To replace the existing code inside the app.js
file, delete all the code currently present and replace it with the following code. This code will create a basic datatable without pagination and search functionality.
import React from "react";
import "./App.css";
import { data } from "./demo-data";
const App = () => {
return (
<div style={{ height: "100vh" }} className="app">
<div className="row justify-content-center align-items-center h-100 m-5 p-2">
<div className="card">
<div className="card-body">
<table className="w-100">
<tr className="border-bottom">
<th className="text-center py-2">Transaction ID</th>
<th className="text-center py-2">Account Number</th>
<th className="text-center py-2">User</th>
<th className="text-center py-2">Date</th>
<th className="text-center py-2">Amount</th>
<th className="text-center py-2">Status</th>
<th className="text-center py-2">Remarks</th>
</tr>
{data.map((transaction) => (
<tr>
<td className="py-2 text-center">
{transaction.transactionId}
</td>
<td className="py-2 text-center">
{transaction.userAccountNumber}
</td>
<td className="py-2 text-center">{transaction.userName}</td>
<td className="py-2 text-center">{transaction.date}</td>
<td className="py-2 text-center">{transaction.amount}</td>
<td className="py-2 text-center px-2">
<span
className={`badge p-2 w-90 text-center rounded-pill bg-${
transaction.status == "pending"
? "warning"
: transaction.status == "completed"
? "success"
: "danger"
}`}
>
{transaction.status}
</span>
</td>
<td className="py-2 text-center">
{transaction.description}
</td>
</tr>
))}
</table>
</div>
</div>
</div>
</div>
);
};
export default App;
Create Sample Data
Create a new file in your project directory and name it demo-data.js
.
Open the demo-data.js
file and paste the demo data inside it.
export const data = [
{
transactionId: "123456789012",
userAccountNumber: "ACCT-123456",
userName: "John Doe",
date: "2023-05-11",
amount: 25.99,
description: "Purchase at XYZ Store",
status: "completed",
},
{
transactionId: "987654321098",
userAccountNumber: "ACCT-789012",
userName: "Jane Smith",
date: "2023-05-10",
amount: 10.5,
description: "Payment received from John Doe",
status: "completed",
},
{
transactionId: "246813579135",
userAccountNumber: "ACCT-345678",
userName: "David Johnson",
date: "2023-05-09",
amount: 150.0,
description: "Monthly utility bill payment",
status: "pending",
},
{
transactionId: "789012345678",
userAccountNumber: "ACCT-901234",
userName: "Sarah Thompson",
date: "2023-05-08",
amount: 50.75,
description: "Online shopping",
status: "completed",
},
{
transactionId: "135792468024",
userAccountNumber: "ACCT-567890",
userName: "Michael Davis",
date: "2023-05-07",
amount: 75.2,
description: "Restaurant bill",
status: "completed",
},
{
transactionId: "468024135792",
userAccountNumber: "ACCT-234567",
userName: "Emily Wilson",
date: "2023-05-06",
amount: 20.5,
description: "Movie ticket",
status: "completed",
},
{
transactionId: "901234567890",
userAccountNumber: "ACCT-678901",
userName: "Daniel Brown",
date: "2023-05-05",
amount: 100.0,
description: "Grocery shopping",
status: "pending",
},
{
transactionId: "246810121416",
userAccountNumber: "ACCT-345678",
userName: "Olivia Martinez",
date: "2023-05-04",
amount: 15.99,
description: "Online subscription",
status: "completed",
},
{
transactionId: "141210864920",
userAccountNumber: "ACCT-789012",
userName: "Christopher Lee",
date: "2023-05-03",
amount: 60.25,
description: "Electronics purchase",
status: "completed",
},
{
transactionId: "181614201210",
userAccountNumber: "ACCT-123456",
userName: "Sophia Clark",
date: "2023-05-02",
amount: 45.8,
description: "Clothing shopping",
status: "pending",
},
{
transactionId: "192837465410",
userAccountNumber: "ACCT-135792",
userName: "Amy Johnson",
date: "2023-05-01",
amount: 30.5,
description: "Coffee shop",
status: "completed",
},
{
transactionId: "102938475610",
userAccountNumber: "ACCT-246813",
userName: "Robert Smith",
date: "2023-04-30",
amount: 75.0,
description: "Gas station",
status: "rejected",
},
{
transactionId: "162534485560",
userAccountNumber: "ACCT-987654",
userName: "Emma Davis",
date: "2023-04-29",
amount: 20.25,
description: "Bookstore",
status: "pending",
},
{
transactionId: "112358132134",
userAccountNumber: "ACCT-579135",
userName: "Jacob Wilson",
date: "2023-04-28",
amount: 50.0,
description: "Charity donation",
status: "completed",
},
{
transactionId: "456789012345",
userAccountNumber: "ACCT-702468",
userName: "Sophia Martinez",
date: "2023-04-27",
amount: 15.99,
description: "Online subscription",
status: "completed",
},
];
Save the demo-data.js
file.
In your app.js
file, import the data
array by adding the following line at the top of the file:
import { data } from "./demo-data";

The simple representation of a datatable can be shown as follows.

Add Pagination and Search Functionality in Datatable in React
To implement pagination and search functionality in the table, we create a separate dataframe called dataToDisplay
. This dataframe stores all the records from the data
array. However, we only display the data that is present in the dataToDisplay
dataframe, which is determined by the current page and the offset size. In simpler terms, the dataToDisplay
dataframe acts as a subset of the original data
array, and we dynamically update this subset based on the chosen page and the number of items to display per page. This allows us to show a limited portion of the data at a time, enabling pagination and efficient searching within the displayed data.
The following code is responsible for handling the movement of our data frame based on the selected page, offset, and search criteria. It ensures that the displayed data corresponds to the appropriate page by calculating the starting and ending indexes of the subset to be displayed. Additionally, it applies the search functionality by filtering the data based on the entered search text. This code ensures that the table content dynamically adjusts as per the user’s navigation through pages, changes in offset (number of items per page), and search input.
useEffect(() => {
let filteredData = data.filter((bank) => {
let b = false;
if (data.length > 0) {
for (let key in data[0]) {
if (
bank[key] &&
bank[key]
.toString()
.toLowerCase()
.includes(searchText.toString().toLowerCase())
) {
b = true;
}
}
}
return b;
});
setPages(Math.floor((filteredData.length + (offset - 1)) / offset));
setTotal(filteredData.length);
const startIdx = 0;
const endIdx = offset - 1;
filteredData = filteredData.filter((bank, idx) => {
return idx <= endIdx && idx >= startIdx;
});
setDataToDisplay(filteredData);
setCurrentPage(1);
}, [searchText]);
useEffect(() => {
let filteredData = data.filter((bank) => {
let b = false;
if (data.length > 0) {
for (let key in data[0]) {
if (
bank[key] &&
bank[key]
.toString()
.toLowerCase()
.includes(searchText.toString().toLowerCase())
) {
b = true;
}
}
}
return b;
});
console.log(filteredData);
const startIdx = (currentPage - 1) * offset;
const endIdx = currentPage * offset - 1;
console.log({ startIdx, endIdx });
filteredData = filteredData.filter((bank, idx) => {
console.log(idx, idx <= endIdx && idx >= startIdx);
return idx <= endIdx && idx >= startIdx;
});
setDataToDisplay(filteredData);
console.log({ startIdx, endIdx });
}, [currentPage]);
// did mount
useEffect(() => {
setPages(Math.floor((data.length + (offset - 1)) / offset));
const startIdx = 0;
const endIdx = offset - 1;
setTotal(data.length);
console.log(startIdx);
console.log(data);
const filteredData = data.filter((bank, idx) => {
return idx <= endIdx && idx >= startIdx;
});
console.log(filteredData);
setDataToDisplay(filteredData);
}, [data, offset]);
All Code in One
import React, { useState, useEffect } from "react";
import "./App.css";
import { data } from "./demo-data";
const App = () => {
const [currentPage, setCurrentPage] = useState(1);
const [searchText, setSearchText] = useState("");
const [offset, setOffset] = useState(10);
const [dataToDisplay, setDataToDisplay] = useState([]);
const [pages, setPages] = useState(0);
const [total, setTotal] = useState(0);
useEffect(() => {
let filteredData = data.filter((bank) => {
let b = false;
if (data.length > 0) {
for (let key in data[0]) {
if (
bank[key] &&
bank[key]
.toString()
.toLowerCase()
.includes(searchText.toString().toLowerCase())
) {
b = true;
}
}
}
return b;
});
setPages(Math.floor((filteredData.length + (offset - 1)) / offset));
setTotal(filteredData.length);
const startIdx = 0;
const endIdx = offset - 1;
filteredData = filteredData.filter((bank, idx) => {
return idx <= endIdx && idx >= startIdx;
});
setDataToDisplay(filteredData);
setCurrentPage(1);
}, [searchText]);
useEffect(() => {
let filteredData = data.filter((bank) => {
let b = false;
if (data.length > 0) {
for (let key in data[0]) {
if (
bank[key] &&
bank[key]
.toString()
.toLowerCase()
.includes(searchText.toString().toLowerCase())
) {
b = true;
}
}
}
return b;
});
console.log(filteredData);
const startIdx = (currentPage - 1) * offset;
const endIdx = currentPage * offset - 1;
console.log({ startIdx, endIdx });
filteredData = filteredData.filter((bank, idx) => {
console.log(idx, idx <= endIdx && idx >= startIdx);
return idx <= endIdx && idx >= startIdx;
});
setDataToDisplay(filteredData);
console.log({ startIdx, endIdx });
}, [currentPage]);
// did mount
useEffect(() => {
setPages(Math.floor((data.length + (offset - 1)) / offset));
const startIdx = 0;
const endIdx = offset - 1;
setTotal(data.length);
console.log(startIdx);
console.log(data);
const filteredData = data.filter((bank, idx) => {
return idx <= endIdx && idx >= startIdx;
});
console.log(filteredData);
setDataToDisplay(filteredData);
}, [data, offset]);
return (
<div style={{ height: "100vh" }} className="app">
<div className="row justify-content-center align-items-center h-100 m-5 p-2">
<div className="card">
<div style={{ maxWidth: "12rem", margin: "10px 0px" }}>
<input
type="text"
className="form-control rounded-0"
aria-label="Notes"
aria-describedby="basic-addon1"
placeholder="Search"
onChange={(e) => {
setSearchText(e.target.value);
}}
value={searchText}
/>
</div>
<div className="card-body m-0 p-0">
<table className="w-100 border">
<tr className="border-bottom">
<th className="text-center py-2">Transaction ID</th>
<th className="text-center py-2">Account Number</th>
<th className="text-center py-2">User</th>
<th className="text-center py-2">Date</th>
<th className="text-center py-2">Amount</th>
<th className="text-center py-2">Status</th>
<th className="text-center py-2">Remarks</th>
</tr>
{dataToDisplay.map((transaction) => (
<tr>
<td className="py-2 text-center">
{transaction.transactionId}
</td>
<td className="py-2 text-center">
{transaction.userAccountNumber}
</td>
<td className="py-2 text-center">{transaction.userName}</td>
<td className="py-2 text-center">{transaction.date}</td>
<td className="py-2 text-center">{transaction.amount}</td>
<td className="py-2 text-center px-2">
<span
className={`badge p-2 w-90 text-center rounded-pill bg-${
transaction.status == "pending"
? "warning"
: transaction.status == "completed"
? "success"
: "danger"
}`}
>
{transaction.status}
</span>
</td>
<td className="py-2 text-center">
{transaction.description}
</td>
</tr>
))}
</table>
</div>
<Pagination
page={currentPage}
limit={pages}
callback={(page) => {
setCurrentPage(page);
}}
count={dataToDisplay.length}
total={total}
callback2={(offsetValue) => {
setOffset(offsetValue);
}}
/>
</div>
</div>
</div>
);
};
const Pagination = ({ page, limit, callback, total, count, callback2 }) => {
const offsetArr = [10, 25, 50, 100, 500, 1000];
return (
<div className="row justify-content-between py-2">
<strong className='col'>{`Showing ${count} of ${total} entries`}</strong>
<div className="d-flex col justify-content-end btn-group">
<select
className="rounded-0 form-select-sm"
aria-label="offset"
onChange={(e) => {
callback2(e.target.value);
}}
>
{offsetArr.map((offset) => (
<option value={offset}>{offset}</option>
))}
</select>
{page - 2 > 0 ? (
<button
onClick={() => {
callback(page - 1);
}}
variant="light"
className="rounded-0 btn-primary"
>
{"Previous"}
</button>
) : null}
{page - 1 > 0 ? (
<button
onClick={() => {
callback(page - 1);
}}
variant="light"
className="rounded-0 btn-primary"
>
{" "}
{(page - 1).toString()}
</button>
) : null}
<button variant="primary"> {page.toString()}</button>
{page + 1 <= limit ? (
<button
onClick={() => {
callback(page + 1);
}}
variant="light"
className="rounded-0 btn-primary"
>
{"Next"}
</button>
) : null}
</div>
</div>
);
};
export default App;
Code Explanation
- The code starts with importing necessary modules and dependencies, including React, useState, useEffect, and some CSS files.
- Next, the code imports the
data
array from thedemo-data
module. - The
App
component is defined using a functional component syntax. - Inside the
App
component, several state variables are declared using theuseState
hook. These variables includecurrentPage
,searchText
,offset
,dataToDisplay
,pages
, andtotal
. These state variables will be used to manage the state of the datatable and pagination. - The first
useEffect
hook is used to filter thedata
array based on thesearchText
state. It calculates the number of pages and updates thepages
andtotal
state variables accordingly. It also sets the initial data to display based on theoffset
value and resets the current page to 1. - The second
useEffect
hook is triggered whenever thecurrentPage
state changes. It filters thedata
array based on the current page and the offset value. It updates thedataToDisplay
state variable accordingly. - The third
useEffect
hook is triggered when the component mounts or when thedata
oroffset
state changes. It calculates the initial number of pages, sets the total count, and initializes thedataToDisplay
state variable with the initial data to show. - The
App
component returns JSX, which represents the structure of the datatable. It includes an input field for searching, a table with columns, and rows rendered based on thedataToDisplay
state. - Finally, there is a
Pagination
component that handles pagination functionality. It receives props such as the current page, limit, callback functions for page change and offset change, and displays buttons for navigating between pages. - The
Pagination
component is then used in theApp
component, rendering it at the bottom of the datatable.
Final Output
By default, the datatable displays 10 records on the first page.

When you click the “Next” button, the datatable will display the next 10 records on the next page.

By typing in the search input box located at the upper left side of the table, the datatable will only display the entries that match the searched text, regardless of the column they belong to.

Congratuations you have successfully created custom Datatable in react js.
If you want to deploy this web application using AWS EC2 and nginx server click here.
Summary
In this article, we will explore the steps involved in creating a custom datatable in React. We will cover the installation of Node.js, the generation of the basic project structure, and the execution of the development server. Additionally, we will highlight the significance of utilizing Bootstrap 5 for styling, which allows us to prioritize the logical aspects of development rather than spending excessive time on CSS.
Furthermore, we will delve into the process of integrating pagination and search functionality into the datatable. By examining code snippets that utilize React hooks like useState and useEffect, we will learn how to implement dynamic data filtering based on search text and enable navigation between different pages of the table.
Throughout the article, we will emphasize the importance of comprehending the code and its purpose. By following the provided instructions and explanations, readers will be able to successfully create custom datatables in ReactJS with pagination and search capabilities. These features significantly enhance the user experience by facilitating efficient management and viewing of large datasets.