Learn how to create pagination component in react.
Whenever we show limited no of items on any particular component, we either use pagination or lazy loading to show more items on certain user actions.
In pagination we divide the total no of items in different pages based on the limit that is provided. If user has to view more items then he needs to navigate to different page.
The navigation can be either done by moving next or prev in which only one change will happen or moving directly to the certain page.
Based on this we will create a simple pagination component in react.
It will be a functional component as we don’t want this component to maintain any state, it should always be controlled by a parent component to match the list of items being shown.
We will be using few extra packages to help us in development.
prop-types: It helps us to validate the props we will receive.classnames: This packages helps us to use CSS classes as javascript objects and add styles conditionally, to make it work we just need to name our CSS file asfilename.module.css.
Following is the folder structure of our component.

Now let us start creating our component. First, import all the required packages at the top.
import React from "react";
import PropTypes from "prop-types";
import styles from "./index.module.css";
import cx from "classnames";
const Pagination = props => {
// Other code will go here
}
export default Pagination;
Before moving any further, first validate the props and set the default ones.
Pagination.propTypes = {
totalItems: PropTypes.number.isRequired,
perPage: PropTypes.number.isRequired,
current: PropTypes.number.isRequired,
onChange: PropTypes.func
};
Pagination.defaultProps = {
current: 1
};
const Pagination = ({perPage, current, onChange, totalItems}) => {
// Other code will go here
}
Our component will be composed of three part
- Previous button:- It will navigate to the prev page.
- List of pages:- We will show no of pages based on the total items and per page requested.
- Next button:- To navigate to the next page.
To generate the number of pages we will have to do a simple mathematics. We will divide the total no of items with items per page and get the ceiling value of it.
Ceiling because even for one item we will need to show a page.
As we need this calculation often, it is better to create a function for this which will be doing this for us.
//Get total no of pages needed
const getTotalPages = () => {
return Math.ceil(totalItems / perPage);
};
With this total count, we can generate the links for the pages.
const total = getTotalPages();
let links = [];
for (let i = 1; i <= total; i++) {
links.push(
<li
onClick={() => direct(i)}
key={i}
className={cx({ [styles.active]: current === i })}
>
{i}
</li>
);
}
return (
<ul className={styles.wrapper}>
<li onClick={prev}><</li>
{links}
<li onClick={next}>></li>
</ul>
);
cx({ [styles.active]: current === i }) will assign the active class on the current page.
lets create three different functions to listen the click events for next, prev, and direct click.
As it is a functional component and should always be controlled by parent, we are following the presentational/container rendering pattern.
In each of the function we will return the current active page number to the parent, along with it I am also returning two extra values for my reference, this is completely optional and you can skip them.
start will return the first number from where items list should start and end will return the last number.
Remember, our pagination is not zero based like an array, it starts from 1.
const next = () => {
const total = getTotalPages();
if (current < total) {
const start = current * perPage;
const end = (current + 1) * perPage;
onChange && onChange({ start, end, current: current + 1 });
}
};
const prev = () => {
const total = getTotalPages();
if (current > 1 && current <= total) {
const start = (current - 2) * perPage;
const end = (current - 1) * perPage;
onChange && onChange({ start, end, current: current - 1 });
}
};
const direct = i => {
if (current !== i) {
const start = (i - 1) * perPage;
const end = i * perPage;
onChange && onChange({ start, end, current: i });
}
};
Complete code of pagination component in react.
import React from "react";
import PropTypes from "prop-types";
import styles from "./index.module.css";
import cx from "classnames";
const Pagination = ({perPage, current, onChange, totalItems}) => {
//Get total no of pages needed
const getTotalPages = () => {
return Math.ceil(totalItems / perPage);
};
const next = () => {
const total = getTotalPages();
if (current < total) {
const start = current * perPage;
const end = (current + 1) * perPage;
onChange && onChange({ start, end, current: current + 1 });
}
};
const prev = () => {
const total = getTotalPages();
if (current > 1 && current <= total) {
const start = (current - 2) * perPage;
const end = (current - 1) * perPage;
onChange && onChange({ start, end, current: current - 1 });
}
};
const direct = i => {
if (current !== i) {
const start = (i - 1) * perPage;
const end = i * perPage;
onChange && onChange({ start, end, current: i });
}
};
const total = getTotalPages();
let links = [];
for (let i = 1; i <= total; i++) {
links.push(
<li
onClick={() => direct(i)}
key={i}
className={cx({ [styles.active]: current === i })}
>
{i}
</li>
);
}
return (
<ul className={styles.wrapper}>
<li onClick={prev}><</li>
{links}
<li onClick={next}>></li>
</ul>
);
};
Pagination.propTypes = {
totalItems: PropTypes.number.isRequired,
perPage: PropTypes.number.isRequired,
current: PropTypes.number.isRequired,
onChange: PropTypes.func
};
Pagination.defaultProps = {
totalItems: 36,
perPage: 5,
current: 1
};
export default Pagination;
Hooray!, we have finished structuring the component, now lets design it.
Complete CSS of pagination component in react.
We will keep the designs very simple and just create the box of pages.
The active page will have an class to show the active state.
//index.module.css
.wrapper {
display: inline-flex;
justify-content: space-between;
align-items: center;
color: #5e6776;
border: 1px solid #607D8B;
padding: 0;
}
.wrapper > li {
background-color: #fff;
width: 20%;
text-align: center;
border-right: 1px solid #607D8B;
padding: 8px 14px;
list-style-type: none;
cursor: pointer;
}
.wrapper > li:last-child {
border: none;
}
.wrapper > li.active {
background-color: #e9e9eb;
}
Input
//test.js
import React, { useState } from "react";
import Pagination from "./App";
const PaginationTest = () => {
const [current, setCurrent] = useState(1);
const onChange = ({ start, end, current }) => {
setCurrent(current);
};
return (
<Pagination
current={current}
onChange={onChange}
totalItems={43}
perPage={7}
/>
);
}
export default PaginationTest;
Output

Pagination with ellipsis hiding unnecessary pages in React
There is another variant to the pagination which was asked in MakeMyTrips SDE2 frontend interview.
In this variant we had to show only:
- First page
- Last page
- Current page
- Previous page (Current page – 1)
- Next page (Current page + 1)
- If any gap between two numbers than we have to show ellipsis
…
Current page: 1, total Page: 10 1 2 … 10 Current page: 5, total Page: 10 1 … 4 5 6 … 10 Current page: 9, total Page: 10 1 … 8 9 10

To achieve this we have to just update the pages list rendering logic, currently we are looping over the total pages and rendering them.
let links = [];
for (let i = 1; i <= total; i++) {
links.push(
<li
onClick={() => direct(i)}
key={i}
className={cx({ [styles.active]: current === i })}
>
{i}
</li>
);
}
We will create a custom function that will take the current page and total no of pages as input and return an array of pages with ellipsis that can be rendered.
All other logic which is in place will work as it is.
As the rule is clear, we will create getPages(currentPage, totalPages) function that will do the processing.
We will implement the following logic within it:
- If the
totalPagesare 1 then return 1 in the array as a base condition. - Get the next and the previous page of the current page.
- Add the first page in the array.
- Check if the absolute difference between first page and the previous page is more than 1 then add the ellipsis after the first page.
- Check if the first page is less than the previous page then only add the previous page.
- If the current page is not equal to the first and the last page then add it.
- Check if the next page is less than the last page then only add the next page.
- Check if the absolute difference between last page and the next page is more than 1 then add the ellipsis after the next page.
- Add the last page in the array.
const getPage = (currentPage, totalPages) => {
// base condition
if(totalPages === 1){
return [1];
}
const pages = [];
const firstPage = 1;
const lastPage = totalPages;
const previousPage = currentPage - 1;
const nextPage = currentPage + 1;
//first page
pages.push(firstPage);
//check and add elipsis
if(Math.abs(firstPage - previousPage) > 1){
pages.push('...')
}
//previous page
if(firstPage < previousPage){
pages.push(previousPage);
}
//current Page
if(currentPage !== firstPage && currentPage !== lastPage){
pages.push(currentPage);
}
//next page
if(nextPage < lastPage){
pages.push(nextPage);
}
//check and add elipsis
if(Math.abs(nextPage - lastPage) > 1){
pages.push('...')
}
//last page
pages.push(lastPage);
return pages;
};
Testcase
console.log(getPage(1, 10)); // [1,2,"...",10] console.log(getPage(5, 10)); // [1,"...",4,5,6,"...",10] console.log(getPage(9, 10)); // [1,"...",8,9,10]
Now update the page rendering logic where we render all the items of the array and add the click event only if the array item is number, ignoring the ellipsis.
const total = getTotalPages();
const links = getPages(current, total).map((e, i) => {
return (
<li
onClick={() => {
if (!isNaN(e)) {
direct(e);
}
}}
key={e + i}
className={cx({ ["active"]: current === e })}
>
{e}
</li>
);
});
This will generate the pagination with ellipsis.

Complete code: Pagination with ellipsis in React.
import cx from "classnames";
const getPages = (currentPage, totalPages) => {
// base condition
if (totalPages === 1) {
return [1];
}
const pages = [];
const firstPage = 1;
const lastPage = totalPages;
const previousPage = currentPage - 1;
const nextPage = currentPage + 1;
//first page
pages.push(firstPage);
//check and add elipsis
if (Math.abs(firstPage - previousPage) > 1) {
pages.push("...");
}
//previous page
if (firstPage < previousPage) {
pages.push(previousPage);
}
//current Page
if (currentPage !== firstPage && currentPage !== lastPage) {
pages.push(currentPage);
}
//next page
if (nextPage < lastPage) {
pages.push(nextPage);
}
//check and add elipsis
if (Math.abs(nextPage - lastPage) > 1) {
pages.push("...");
}
//last page
pages.push(lastPage);
return pages;
};
const Pagination = ({ perPage, current, onChange, totalItems }) => {
//Get total no of pages needed
const getTotalPages = () => {
return Math.ceil(totalItems / perPage);
};
const next = () => {
const total = getTotalPages();
if (current < total) {
const start = current * perPage;
const end = (current + 1) * perPage;
onChange && onChange({ start, end, current: current + 1 });
}
};
const prev = () => {
const total = getTotalPages();
if (current > 1 && current <= total) {
const start = (current - 2) * perPage;
const end = (current - 1) * perPage;
onChange && onChange({ start, end, current: current - 1 });
}
};
const direct = (i) => {
if (current !== i) {
const start = (i - 1) * perPage;
const end = i * perPage;
onChange && onChange({ start, end, current: i });
}
};
const total = getTotalPages();
const links = getPages(current, total).map((e, i) => {
return (
<li
onClick={() => {
if (!isNaN(e)) {
direct(e);
}
}}
key={e + i}
className={cx({ ["active"]: current === e })}
>
{e}
</li>
);
});
return (
<ul className={"wrapper"}>
<li onClick={prev}><</li>
{links}
<li onClick={next}>></li>
</ul>
);
};
export default Pagination;