Mastering Pagination in React: Build Efficient, User-Friendly Lists
Last updated 2 months, 2 weeks ago | 66 views 75 5

Introduction: Why React Pagination Matters
When building a data-heavy web application—such as a blog, product catalog, or dashboard—displaying all records at once can lead to poor performance and a bad user experience.
That’s where pagination comes in.
Pagination allows you to split large data sets into smaller chunks across multiple pages, making the UI faster, more navigable, and scalable. It improves rendering speed, saves bandwidth, and helps users consume content efficiently.
This article walks you through implementing both client-side and API-driven (server-side) pagination in React using useState
, useEffect
, and clean reusable components.
Understanding Pagination in React
What is Pagination?
Pagination is a technique to break a list of items into discrete pages. In a UI, this is usually presented with Previous/Next buttons, page numbers, or both.
Step-by-Step: Implementing Client-Side Pagination in React
Let’s create a basic paginated list of items stored in memory.
✅ Step 1: Set Up State and Items
import React, { useState } from 'react';
// Sample list of items
const items = Array.from({ length: 45 }, (_, i) => `Item ${i + 1}`);
function PaginatedList() {
const [currentPage, setCurrentPage] = useState(1);
const itemsPerPage = 10;
// Calculate indexes for slicing
const indexOfLastItem = currentPage * itemsPerPage;
const indexOfFirstItem = indexOfLastItem - itemsPerPage;
const currentItems = items.slice(indexOfFirstItem, indexOfLastItem);
// Total number of pages
const totalPages = Math.ceil(items.length / itemsPerPage);
// Page number array
const pageNumbers = [...Array(totalPages).keys()].map(n => n + 1);
return (
<div>
<h2>Paginated List</h2>
<ul>
{currentItems.map(item => (
<li key={item}>{item}</li>
))}
</ul>
<div className="pagination">
{pageNumbers.map(number => (
<button
key={number}
onClick={() => setCurrentPage(number)}
className={number === currentPage ? 'active' : ''}
>
{number}
</button>
))}
</div>
</div>
);
}
export default PaginatedList;
Explanation:
-
We slice the full list (
items
) to show only the items for the current page. -
We generate buttons dynamically based on the number of total pages.
-
We handle pagination manually using
useState
.
Server-Side/API Pagination (React + useEffect + fetch)
If you're working with paginated API responses, here's how to implement that using useEffect
and query parameters.
✅ Step 1: Fetch API Data with Pagination Parameters
import React, { useState, useEffect } from 'react';
function APIPagination() {
const [data, setData] = useState([]);
const [page, setPage] = useState(1);
const itemsPerPage = 10;
useEffect(() => {
fetch(`https://jsonplaceholder.typicode.com/posts?_page=${page}&_limit=${itemsPerPage}`)
.then(res => res.json())
.then(json => setData(json));
}, [page]);
return (
<div>
<h2>Paginated API Results</h2>
<ul>
{data.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
<div className="pagination">
<button onClick={() => setPage(p => Math.max(p - 1, 1))}>Previous</button>
<span> Page {page} </span>
<button onClick={() => setPage(p => p + 1)}>Next</button>
</div>
</div>
);
}
export default APIPagination;
Adjust API URLs and response logic based on your own backend format.
✅ Full Example: Simple Pagination Component
Here’s a modular and reusable component that you can plug into any React project.
// Pagination.js
import React from 'react';
function Pagination({ totalPages, currentPage, onPageChange }) {
return (
<div className="pagination">
<button onClick={() => onPageChange(currentPage - 1)} disabled={currentPage === 1}>
Prev
</button>
{[...Array(totalPages).keys()].map(num => {
const page = num + 1;
return (
<button
key={page}
onClick={() => onPageChange(page)}
className={page === currentPage ? 'active' : ''}
>
{page}
</button>
);
})}
<button onClick={() => onPageChange(currentPage + 1)} disabled={currentPage === totalPages}>
Next
</button>
</div>
);
}
export default Pagination;
Tips & Common Pitfalls
✅ Best Practices
-
Keep pagination UI reusable via props.
-
Use server-side pagination for large datasets.
-
Always show a loading indicator when fetching new pages.
-
Preserve scroll position or use smooth scrolling.
❌ Common Pitfalls
Mistake | Problem | Solution |
---|---|---|
Not handling Next /Prev limits |
Causes page number overflow or crashes | Use Math.min() and Math.max() bounds |
Rendering all items | Causes UI lag and memory issues | Always slice or use backend limits |
Using index as key | May break re-renders | Use unique IDs where possible |
Comparison: Client vs Server Pagination
Feature | Client-Side | Server-Side |
---|---|---|
Performance | Slower with large data | Fast, scalable |
Initial Data | Loaded all at once | Fetched per request |
Control | Fully handled in frontend | Backend controls limit/offset |
Use Case | Small apps, static data | Large data sets, APIs |
Conclusion: Pagination That Scales with You
Pagination in React isn't just about slicing arrays—it's about optimizing performance, maintaining UX, and scaling for growth. Whether you're managing local data or fetching from an API, proper pagination ensures your users aren’t overwhelmed and your app runs smoothly.
Takeaways:
Use
useState
+slice()
for small setsUse
useEffect
+ API calls for scalable appsBuild reusable pagination components