Mastering Pagination in React: Build Efficient, User-Friendly Lists

Last updated 2 months, 2 weeks ago | 66 views 75     5

Tags:- React

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 sets

  • Use useEffect + API calls for scalable apps

  • Build reusable pagination components