React - Pagination Example with Logic like Google
Built with React 16.3.1
Other versions available:
- React: React + Node
- Angular: Angular 10, 9, 8, 2/5, Angular + Node
- Vue: Vue, Vue + Node
- AngularJS: AngularJS
- ASP.NET: Razor Pages, ASP.NET MVC
UPDATE 10 Apr 2018 - The pagination component is now available on npm, for details check out npm - JW React Pagination Component.
This is an example of how to implement pagination in React and ES6 / JavaScript with logic like Google's search results.
Project is available on GitHub at https://github.com/cornflourblue/react-pagination-example.
Here it is in action: (See on CodePen at http://codepen.io/cornflourblue/pen/oZZzLr/)
Update History:
- 23 Apr 2018 - Added pageSize prop to pagination component
- 10 Apr 2018 - Updated to React 16.3.1, updated to Webpack 4.5, removed dependency on underscore.js / lodash, published pagination component to npm.
- 14 Mar 2017 - Built with React 15.4.2
The React code on CodePen is slightly different to the code on GitHub, on CodePen all the React components are in same file / window because CodePen only has a single window for JS, the GitHub repo is structured like a real application with the React components in separate JSX files, this is the better way to structure your React project. Also the example on CodePen transpiles the ES6 into JS in the browser which is only good for demos and not production applications.
Google's Pagination Logic
The logic in Google's pagination is as follows:
- there are 10 page links shown at any time (e.g. 1 2 3 4 5 6 7 8 9 10) unless there are less than 10 total pages
- the active link (current page) is in the 6th position, except for when the active link is below 6 or less than 4 from the last position
Here's what it looks like for each page if there are 15 total pages:
[1] 2 3 4 5 6 7 8 9 10
1 [2] 3 4 5 6 7 8 9 10
1 2 [3] 4 5 6 7 8 9 10
1 2 3 [4] 5 6 7 8 9 10
1 2 3 4 [5] 6 7 8 9 10
1 2 3 4 5 [6] 7 8 9 10
2 3 4 5 6 [7] 8 9 10 11
3 4 5 6 7 [8] 9 10 11 12
4 5 6 7 8 [9] 10 11 12 13
5 6 7 8 9 [10] 11 12 13 14
6 7 8 9 10 [11] 12 13 14 15
6 7 8 9 10 11 [12] 13 14 15
6 7 8 9 10 11 12 [13] 14 15
6 7 8 9 10 11 12 13 [14] 15
6 7 8 9 10 11 12 13 14 [15]
Running the React Pagination Example Locally
The example uses webpack to transpile / compile the ES6 code and build the project, webpack is an npm package that runs on Node.
- Install NodeJS and NPM from https://nodejs.org/en/download/.
- Download the React project source code from GitHub at https://github.com/cornflourblue/react-pagination-example
- Install all required npm packages by running
npm install
from the command line in the project root folder (where the package.json is located). - Start the application by running
npm start
from the command line in the project root folder, this will start the webpack dev server which serves the example from port 8080 on your local machine. - Browse to the url http://localhost:8080 to test the React pagination example
React Pagination Component
The React pagination component is a self contained reusable component that can be used to enable paging through any list of items. As properties it takes the array of items to be paged and an onChangePage()
callback function to notify the parent component when the page is changed. It also has an optional property to set the initial page displayed which defaults to the first page, and the page size which defaults to 10.
When the paged items array is updated after initial rendering (e.g. after an async http request to a web api) the pagination component resets the page in the react componentDidUpdate()
lifecycle function, causing the pagination component to re-render and notify the parent component via the onChangePage()
callback;
I've structured the code according to the recommended best practices on the Airbnb React/JSX Style Guide.
import React from 'react';
import PropTypes from 'prop-types';
const propTypes = {
items: PropTypes.array.isRequired,
onChangePage: PropTypes.func.isRequired,
initialPage: PropTypes.number,
pageSize: PropTypes.number
}
const defaultProps = {
initialPage: 1,
pageSize: 10
}
class Pagination extends React.Component {
constructor(props) {
super(props);
this.state = { pager: {} };
}
componentWillMount() {
// set page if items array isn't empty
if (this.props.items && this.props.items.length) {
this.setPage(this.props.initialPage);
}
}
componentDidUpdate(prevProps, prevState) {
// reset page if items array has changed
if (this.props.items !== prevProps.items) {
this.setPage(this.props.initialPage);
}
}
setPage(page) {
var { items, pageSize } = this.props;
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, pageSize);
// 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 = [...Array((endPage + 1) - startPage).keys()].map(i => startPage + i);
// 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;
if (!pager.pages || pager.pages.length <= 1) {
// don't display pager if there is only 1 page
return null;
}
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 = defaultProps;
export default Pagination;
React App Component that uses the Pagination Component
An example react component that uses the above pagination component to paginate a list of 150 example items that are generated using the Array object and spread operator.
import React from 'react';
import Pagination from './Pagination';
class App extends React.Component {
constructor() {
super();
// an example array of 150 items to be paged
var exampleItems = [...Array(150).keys()].map(i => ({ id: (i+1), name: 'Item ' + (i+1) }));
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;
Need Some React Help?
Search fiverr for freelance React developers.
Follow me for updates
When I'm not coding...
Me and Tina are on a motorcycle adventure around Australia.
Come along for the ride!