[reactjs] how to implement Pagination in reactJs

I am new to ReactJS and am creating a simple TODO application in it. Actually, it is a very basic app with no db connection, tasks are stored in an array. I added Edit and Delete functionality to it now I want to add pagination. How do I implement it? Any help will be appreciated. Thank you...!!

This question is related to reactjs pagination

The answer is


   Sample pagination react js working code 
    import React, { Component } from 'react';
    import {
    Pagination,
    PaginationItem,
    PaginationLink
    } from "reactstrap";


    let prev  = 0;
    let next  = 0;
    let last  = 0;
    let first = 0;
    export default class SamplePagination extends Component {
       constructor() {
         super();
         this.state = {
           todos: ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','T','v','u','w','x','y','z'],
           currentPage: 1,
           todosPerPage: 3,

         };
         this.handleClick = this.handleClick.bind(this);
         this.handleLastClick = this.handleLastClick.bind(this);
         this.handleFirstClick = this.handleFirstClick.bind(this);
       }

       handleClick(event) {
         event.preventDefault();
         this.setState({
           currentPage: Number(event.target.id)
         });
       }

       handleLastClick(event) {
         event.preventDefault();
         this.setState({
           currentPage:last
         });
       }
       handleFirstClick(event) {
         event.preventDefault();
         this.setState({
           currentPage:1
         });
       }
       render() {
         let { todos, currentPage, todosPerPage } = this.state;

         // Logic for displaying current todos
         let indexOfLastTodo = currentPage * todosPerPage;
         let indexOfFirstTodo = indexOfLastTodo - todosPerPage;
         let currentTodos = todos.slice(indexOfFirstTodo, indexOfLastTodo);
          prev  = currentPage > 0 ? (currentPage -1) :0;
          last = Math.ceil(todos.length/todosPerPage);
          next  = (last === currentPage) ?currentPage: currentPage +1;

         // Logic for displaying page numbers
         let pageNumbers = [];
         for (let i = 1; i <=last; i++) {
           pageNumbers.push(i);
         }

          return (
           <div>
             <ul>
              {
                currentTodos.map((todo,index) =>{
                  return <li key={index}>{todo}</li>;
                })
              }
             </ul><ul id="page-numbers">
             <nav>
              <Pagination>
              <PaginationItem>
              { prev === 0 ? <PaginationLink disabled>First</PaginationLink> :
                  <PaginationLink onClick={this.handleFirstClick} id={prev} href={prev}>First</PaginationLink>
              }
              </PaginationItem>
              <PaginationItem>
              { prev === 0 ? <PaginationLink disabled>Prev</PaginationLink> :
                  <PaginationLink onClick={this.handleClick} id={prev} href={prev}>Prev</PaginationLink>
              }
              </PaginationItem>
                 {
                  pageNumbers.map((number,i) =>
                  <Pagination key= {i}>
                  <PaginationItem active = {pageNumbers[currentPage-1] === (number) ? true : false} >
                   <PaginationLink onClick={this.handleClick} href={number} key={number} id={number}>
                   {number}
                   </PaginationLink>
                   </PaginationItem>
                  </Pagination>
                )}

             <PaginationItem>
             {
               currentPage === last ? <PaginationLink disabled>Next</PaginationLink> :
               <PaginationLink onClick={this.handleClick} id={pageNumbers[currentPage]} href={pageNumbers[currentPage]}>Next</PaginationLink>
             }
             </PaginationItem>

             <PaginationItem>
             {
               currentPage === last ? <PaginationLink disabled>Last</PaginationLink> :
               <PaginationLink onClick={this.handleLastClick} id={pageNumbers[currentPage]} href={pageNumbers[currentPage]}>Last</PaginationLink>
             }
             </PaginationItem>
             </Pagination>
              </nav>
             </ul>
           </div>
         );
       }
     }

     ReactDOM.render(
      <SamplePagination />,
      document.getElementById('root')
    );

I've recently created library which helps to cope with pagination cases like:

  • storing normalized data in Redux
  • caching pages based on search filters
  • simplified react-virtualized list usage
  • refreshing results in background
  • storing last visited page and used filters

DEMO page implements all above features.

Source code you can find on Github


I have implemented pagination + search in ReactJs, see the output: Pagination in React

View complete code on GitHub: https://github.com/navanathjadhav/generic-pagination

Also visit this article for step by step implementation of pagination: https://everblogs.com/react/3-simple-steps-to-add-pagination-in-react/


Give you a pagination component, which is maybe a little difficult to understand for newbie to react:

https://www.npmjs.com/package/pagination


I have tried to recreate the simple pagination example given by piotr-berebecki which was great. But when there will be a lot of pages then the pagination will overflow in the screen. So, I used previous and back button along with forward and backward button to stream back and forth between the pages. And for design part I have used bootstrap 3.

You can customize no of pages to display in pagination using the pagebound values. Make sure to use same value for upperPageBound and pageBound.

    class TodoApp extends React.Component {
          constructor() {
            super();
            this.state = {
              todos: ['a','b','c','d','e','f','g','h','i','j','k','l','m',
'n','o','p','q','r','s','t','u','v','w','x','y','z'],
              currentPage: 1,
              todosPerPage: 3,
              upperPageBound: 3,
              lowerPageBound: 0,
              isPrevBtnActive: 'disabled',
              isNextBtnActive: '',
              pageBound: 3
            };
            this.handleClick = this.handleClick.bind(this);
            this.btnDecrementClick = this.btnDecrementClick.bind(this);
            this.btnIncrementClick = this.btnIncrementClick.bind(this);
            this.btnNextClick = this.btnNextClick.bind(this);
            this.btnPrevClick = this.btnPrevClick.bind(this);
            // this.componentDidMount = this.componentDidMount.bind(this);
            this.setPrevAndNextBtnClass = this.setPrevAndNextBtnClass.bind(this);
          }
          componentDidUpdate() {
                $("ul li.active").removeClass('active');
                $('ul li#'+this.state.currentPage).addClass('active');
          }
          handleClick(event) {
            let listid = Number(event.target.id);
            this.setState({
              currentPage: listid
            });
            $("ul li.active").removeClass('active');
            $('ul li#'+listid).addClass('active');
            this.setPrevAndNextBtnClass(listid);
          }
          setPrevAndNextBtnClass(listid) {
            let totalPage = Math.ceil(this.state.todos.length / this.state.todosPerPage);
            this.setState({isNextBtnActive: 'disabled'});
            this.setState({isPrevBtnActive: 'disabled'});
            if(totalPage === listid && totalPage > 1){
                this.setState({isPrevBtnActive: ''});
            }
            else if(listid === 1 && totalPage > 1){
                this.setState({isNextBtnActive: ''});
            }
            else if(totalPage > 1){
                this.setState({isNextBtnActive: ''});
                this.setState({isPrevBtnActive: ''});
            }
        }
          btnIncrementClick() {
              this.setState({upperPageBound: this.state.upperPageBound + this.state.pageBound});
              this.setState({lowerPageBound: this.state.lowerPageBound + this.state.pageBound});
              let listid = this.state.upperPageBound + 1;
              this.setState({ currentPage: listid});
              this.setPrevAndNextBtnClass(listid);
        }
          btnDecrementClick() {
            this.setState({upperPageBound: this.state.upperPageBound - this.state.pageBound});
            this.setState({lowerPageBound: this.state.lowerPageBound - this.state.pageBound});
            let listid = this.state.upperPageBound - this.state.pageBound;
            this.setState({ currentPage: listid});
            this.setPrevAndNextBtnClass(listid);
        }
        btnPrevClick() {
            if((this.state.currentPage -1)%this.state.pageBound === 0 ){
                this.setState({upperPageBound: this.state.upperPageBound - this.state.pageBound});
                this.setState({lowerPageBound: this.state.lowerPageBound - this.state.pageBound});
            }
            let listid = this.state.currentPage - 1;
            this.setState({ currentPage : listid});
            this.setPrevAndNextBtnClass(listid);
        }
        btnNextClick() {
            if((this.state.currentPage +1) > this.state.upperPageBound ){
                this.setState({upperPageBound: this.state.upperPageBound + this.state.pageBound});
                this.setState({lowerPageBound: this.state.lowerPageBound + this.state.pageBound});
            }
            let listid = this.state.currentPage + 1;
            this.setState({ currentPage : listid});
            this.setPrevAndNextBtnClass(listid);
        }
          render() {
            const { todos, currentPage, todosPerPage,upperPageBound,lowerPageBound,isPrevBtnActive,isNextBtnActive } = this.state;
            // Logic for displaying current todos
            const indexOfLastTodo = currentPage * todosPerPage;
            const indexOfFirstTodo = indexOfLastTodo - todosPerPage;
            const currentTodos = todos.slice(indexOfFirstTodo, indexOfLastTodo);

            const renderTodos = currentTodos.map((todo, index) => {
              return <li key={index}>{todo}</li>;
            });

            // Logic for displaying page numbers
            const pageNumbers = [];
            for (let i = 1; i <= Math.ceil(todos.length / todosPerPage); i++) {
              pageNumbers.push(i);
            }

            const renderPageNumbers = pageNumbers.map(number => {
                if(number === 1 && currentPage === 1){
                    return(
                        <li key={number} className='active' id={number}><a href='#' id={number} onClick={this.handleClick}>{number}</a></li>
                    )
                }
                else if((number < upperPageBound + 1) && number > lowerPageBound){
                    return(
                        <li key={number} id={number}><a href='#' id={number} onClick={this.handleClick}>{number}</a></li>
                    )
                }
            });
            let pageIncrementBtn = null;
            if(pageNumbers.length > upperPageBound){
                pageIncrementBtn = <li className=''><a href='#' onClick={this.btnIncrementClick}> &hellip; </a></li>
            }
            let pageDecrementBtn = null;
            if(lowerPageBound >= 1){
                pageDecrementBtn = <li className=''><a href='#' onClick={this.btnDecrementClick}> &hellip; </a></li>
            }
            let renderPrevBtn = null;
            if(isPrevBtnActive === 'disabled') {
                renderPrevBtn = <li className={isPrevBtnActive}><span id="btnPrev"> Prev </span></li>
            }
            else{
                renderPrevBtn = <li className={isPrevBtnActive}><a href='#' id="btnPrev" onClick={this.btnPrevClick}> Prev </a></li>
            }
            let renderNextBtn = null;
            if(isNextBtnActive === 'disabled') {
                renderNextBtn = <li className={isNextBtnActive}><span id="btnNext"> Next </span></li>
            }
            else{
                renderNextBtn = <li className={isNextBtnActive}><a href='#' id="btnNext" onClick={this.btnNextClick}> Next </a></li>
            }
            return (
              <div>
                  <ul>
                  {renderTodos}
                </ul>
                <ul id="page-numbers" className="pagination">
                  {renderPrevBtn}
                  {pageDecrementBtn}
                  {renderPageNumbers}
                  {pageIncrementBtn}
                  {renderNextBtn}
                </ul>
              </div>
            );
          }
        }


        ReactDOM.render(
          <TodoApp />,
          document.getElementById('app')
        );

Working demo link : https://codepen.io/mhmanandhar/pen/oEWBqx

Image : simple react pagination


Please see this code sample in codesandbox

https://codesandbox.io/s/pagino-13pit

enter image description here

import Pagino from "pagino";
import { useState, useMemo } from "react";

export default function App() {
  const [pages, setPages] = useState([]);

  const pagino = useMemo(() => {
    const _ = new Pagino({
      showFirst: false,
      showLast: false,
      onChange: (page, count) => setPages(_.getPages())
    });

    _.setCount(10);

    return _;
  }, []);

  const hanglePaginoNavigation = (type) => {
    if (typeof type === "string") {
      pagino[type]?.();
      return;
    }

    pagino.setPage(type);
  };

  return (
    <div>
      <h1>Page: {pagino.page}</h1>
      <ul>
        {pages.map((page) => (
          <button key={page} onClick={() => hanglePaginoNavigation(page)}>
            {page}
          </button>
        ))}
      </ul>
    </div>
  );
}

A ReactJS dumb component to render pagination. You can also use this Library:

https://www.npmjs.com/package/react-js-pagination

Some Examples are Here:

http://vayser.github.io/react-js-pagination/

OR

You can also Use this https://codepen.io/PiotrBerebecki/pen/pEYPbY


Make sure you make it as a separate component I have used tabler-react

import * as React from "react";
import { Page,  Button } from "tabler-react";
class PaginateComponent extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            array: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
            limit: 5, // optional
            page: 1
        };
    }

paginateValue = (page) => {
    this.setState({ page: page });
    console.log(page) // access this value from parent component 
}

paginatePrevValue = (page) => {
    this.setState({ page: page });
    console.log(page)  // access this value from parent component
}
paginateNxtValue = (page) => {
       this.setState({ page: page });
       console.log(page)  // access this value from parent component
    }

    render() {
        return (
            <div>    
                <div>
                    <Button.List>
                        <Button
                      disabled={this.state.page === 0}
                      onClick={() => this.paginatePrevValue(this.state.page - 1)}
                            outline
                            color="primary"
                        >
                            Previous
                      </Button>

                        {this.state.array.map((value, index) => {
                            return (
                                <Button
                                    onClick={() => this.paginateValue(value)}
                                    color={
                                        this.state.page === value
                                            ? "primary"
                                            : "secondary"
                                    }
                                >
                                    {value}
                                </Button>
                            );
                        })}

                        <Button
                            onClick={() => this.paginateNxtValue(this.state.page + 1)}
                            outline
                            color="secondary"
                        >
                            Next
                      </Button>
                    </Button.List>
                </div>
            </div>

        )
    }
}

export default PaginateComponent;

Here is a way to create your Custom Pagination Component from react-bootstrap lib and this component you can use Throughout your project enter image description here

Your Pagination Component (pagination.jsx or js)

import React, { Component } from "react"; 
import { Pagination } from "react-bootstrap"; 
import PropTypes from "prop-types";

export default class PaginationHandler extends Component {  

   constructor(props) {
    super(props);
    this.state = {
      paging: {
        offset: 0,
        limit: 10
      },
      active: 0
    };  
    }

  pagingHandler = () => {
    let offset = parseInt(event.target.id);
    this.setState({
      active: offset
    });
    this.props.pageHandler(event.target.id - 1);   };

  nextHandler = () => {
    let active = this.state.active;
    this.setState({
      active: active + 1
    });
    this.props.pageHandler(active + 1);   };

  backHandler = () => {
    let active = this.state.active;
    this.setState({
      active: active - 1
    });
    this.props.pageHandler(active - 1);   };

  renderPageNumbers = (pageNumbers, totalPages) => {
    let { active } = this.state;
    return (
      <Pagination>
        <Pagination.Prev disabled={active < 5} onClick={ active >5 && this.backHandler} />

        {
      pageNumbers.map(number => {
              if (
                number >= parseInt(active) - 3 &&
                number <= parseInt(active) + 3 
              ) {
                return (
                  <Pagination.Item
                    id={number}
                    active={number == active}
                    onClick={this.pagingHandler}
                  >
                    {number}
                  </Pagination.Item>
                );
              } else {
                return null;
              }
        })}
        <Pagination.Next onClick={ active <= totalPages -4 && this.nextHandler} />
      </Pagination>
    );   };

  buildComponent = (props, state) => {
    const { totalPages } = props;
    const pageNumbers = [];
    for (let i = 1; i <= totalPages; i++) {
      pageNumbers.push(i);
    }
    return (
      <div className="pull-right">
      {this.renderPageNumbers(pageNumbers ,totalPages)}
      </div>
    );   
};

  render() {
    return this.buildComponent(this.props, this.state);
  } 

} 
   PaginationHandler.propTypes = 
   {
    paging: PropTypes.object,
    pageHandler: PropTypes.func,
    totalPages: PropTypes.object 
   };

Use of Above Component in your Component

import Pagination from "../pagination";

pageHandler = (offset) =>{
      this.setState(({ paging }) => ({
        paging: { ...paging, offset: offset }
      }));
   }
   render() {
    return (
      <div>
         <Pagination
          paging = {paging}
          pageHandler = {this.pageHandler}
          totalPages = {totalPages}>
          </Pagination>
      </div>
    );
  }

I recently created this Pagination component that implements paging logic like Google's search results:

import React, { PropTypes } from 'react';
 
const propTypes = {
    items: PropTypes.array.isRequired,
    onChangePage: PropTypes.func.isRequired,
    initialPage: PropTypes.number   
}
 
const defaultProps = {
    initialPage: 1
}
 
class Pagination extends React.Component {
    constructor(props) {
        super(props);
        this.state = { pager: {} };
    }
 
    componentWillMount() {
        this.setPage(this.props.initialPage);
    }
 
    setPage(page) {
        var items = this.props.items;
        var pager = this.state.pager;
 
        if (page < 1 || page > pager.totalPages) {
            return;
        }
 
        // get new pager object for specified page
        pager = this.getPager(items.length, page);
 
        // get new page of items from items array
        var pageOfItems = items.slice(pager.startIndex, pager.endIndex + 1);
 
        // update state
        this.setState({ pager: pager });
 
        // call change page function in parent component
        this.props.onChangePage(pageOfItems);
    }
 
    getPager(totalItems, currentPage, pageSize) {
        // default to first page
        currentPage = currentPage || 1;
 
        // default page size is 10
        pageSize = pageSize || 10;
 
        // calculate total pages
        var totalPages = Math.ceil(totalItems / pageSize);
 
        var startPage, endPage;
        if (totalPages <= 10) {
            // less than 10 total pages so show all
            startPage = 1;
            endPage = totalPages;
        } else {
            // more than 10 total pages so calculate start and end pages
            if (currentPage <= 6) {
                startPage = 1;
                endPage = 10;
            } else if (currentPage + 4 >= totalPages) {
                startPage = totalPages - 9;
                endPage = totalPages;
            } else {
                startPage = currentPage - 5;
                endPage = currentPage + 4;
            }
        }
 
        // calculate start and end item indexes
        var startIndex = (currentPage - 1) * pageSize;
        var endIndex = Math.min(startIndex + pageSize - 1, totalItems - 1);
 
        // create an array of pages to ng-repeat in the pager control
        var pages = _.range(startPage, endPage + 1);
 
        // return object with all pager properties required by the view
        return {
            totalItems: totalItems,
            currentPage: currentPage,
            pageSize: pageSize,
            totalPages: totalPages,
            startPage: startPage,
            endPage: endPage,
            startIndex: startIndex,
            endIndex: endIndex,
            pages: pages
        };
    }
 
    render() {
        var pager = this.state.pager;
 
        return (
            <ul className="pagination">
                <li className={pager.currentPage === 1 ? 'disabled' : ''}>
                    <a onClick={() => this.setPage(1)}>First</a>
                </li>
                <li className={pager.currentPage === 1 ? 'disabled' : ''}>
                    <a onClick={() => this.setPage(pager.currentPage - 1)}>Previous</a>
                </li>
                {pager.pages.map((page, index) =>
                    <li key={index} className={pager.currentPage === page ? 'active' : ''}>
                        <a onClick={() => this.setPage(page)}>{page}</a>
                    </li>
                )}
                <li className={pager.currentPage === pager.totalPages ? 'disabled' : ''}>
                    <a onClick={() => this.setPage(pager.currentPage + 1)}>Next</a>
                </li>
                <li className={pager.currentPage === pager.totalPages ? 'disabled' : ''}>
                    <a onClick={() => this.setPage(pager.totalPages)}>Last</a>
                </li>
            </ul>
        );
    }
}
 
Pagination.propTypes = propTypes;
Pagination.defaultProps
export default Pagination;

And here's an example App component that uses the Pagination component to paginate a list of 150 example items:

import React from 'react';
import Pagination from './Pagination';
 
class App extends React.Component {
    constructor() {
        super();
 
        // an example array of items to be paged
        var exampleItems = _.range(1, 151).map(i => { return { id: i, name: 'Item ' + i }; });
 
        this.state = {
            exampleItems: exampleItems,
            pageOfItems: []
        };
 
        // bind function in constructor instead of render (https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md)
        this.onChangePage = this.onChangePage.bind(this);
    }
 
    onChangePage(pageOfItems) {
        // update state with new page of items
        this.setState({ pageOfItems: pageOfItems });
    }
 
    render() {
        return (
            <div>
                <div className="container">
                    <div className="text-center">
                        <h1>React - Pagination Example with logic like Google</h1>
                        {this.state.pageOfItems.map(item =>
                            <div key={item.id}>{item.name}</div>
                        )}
                        <Pagination items={this.state.exampleItems} onChangePage={this.onChangePage} />
                    </div>
                </div>
                <hr />
                <div className="credits text-center">
                    <p>
                        <a href="http://jasonwatmore.com" target="_top">JasonWatmore.com</a>
                    </p>
                </div>
            </div>
        );
    }
}
 
export default App;

For more details and a live demo you can check out this post