How to make tables paginated in React

Pagination improves performance and user experience when displaying large datasets by breaking data into manageable chunks and reducing initial render load. As the creator of CoreUI, a widely used open-source UI library, I’ve implemented pagination in countless data tables throughout my 11 years of frontend development. The most efficient approach is managing current page state and slicing the data array to display only the relevant subset. This method provides responsive pagination controls with clear navigation and maintains optimal rendering performance for large datasets.

Track current page in state, calculate page slice indices, and render pagination controls with next/previous navigation.

import { useState } from 'react'

const PaginatedTable = ({ data }) => {
  const [currentPage, setCurrentPage] = useState(1)
  const itemsPerPage = 10

  const totalPages = Math.ceil(data.length / itemsPerPage)
  const startIndex = (currentPage - 1) * itemsPerPage
  const endIndex = startIndex + itemsPerPage
  const currentData = data.slice(startIndex, endIndex)

  const goToPage = (page) => {
    setCurrentPage(Math.max(1, Math.min(page, totalPages)))
  }

  const renderPageNumbers = () => {
    const pages = []
    for (let i = 1; i <= totalPages; i++) {
      if (
        i === 1 ||
        i === totalPages ||
        (i >= currentPage - 1 && i <= currentPage + 1)
      ) {
        pages.push(
          <button
            key={i}
            onClick={() => goToPage(i)}
            className={currentPage === i ? 'active' : ''}
          >
            {i}
          </button>
        )
      } else if (pages[pages.length - 1]?.key !== 'ellipsis') {
        pages.push(<span key={`ellipsis-${i}`}>...</span>)
      }
    }
    return pages
  }

  return (
    <div>
      <table>
        <thead>
          <tr>
            <th>ID</th>
            <th>Name</th>
            <th>Email</th>
            <th>Role</th>
          </tr>
        </thead>
        <tbody>
          {currentData.map(user => (
            <tr key={user.id}>
              <td>{user.id}</td>
              <td>{user.name}</td>
              <td>{user.email}</td>
              <td>{user.role}</td>
            </tr>
          ))}
        </tbody>
      </table>

      <div className='pagination'>
        <button
          onClick={() => goToPage(currentPage - 1)}
          disabled={currentPage === 1}
        >
          Previous
        </button>

        {renderPageNumbers()}

        <button
          onClick={() => goToPage(currentPage + 1)}
          disabled={currentPage === totalPages}
        >
          Next
        </button>

        <span>
          Page {currentPage} of {totalPages} ({data.length} total items)
        </span>
      </div>
    </div>
  )
}

Here the currentPage state tracks which page is currently displayed. The totalPages calculation determines how many pages are needed based on total data length and itemsPerPage. The slice method extracts only the data subset for the current page using calculated startIndex and endIndex. The goToPage function ensures page number stays within valid bounds using Math.max and Math.min. The renderPageNumbers function creates smart pagination showing first page, last page, current page with neighbors, and ellipsis for gaps. Previous and Next buttons are disabled at boundaries to prevent invalid navigation.

Best Practice Note:

This is the pagination pattern we use in CoreUI table components for efficient data navigation in enterprise dashboards. Combine pagination with sorting and filtering by applying those operations before calculating pagination indices, implement URL-based pagination using query parameters to enable shareable links to specific pages, and consider virtual scrolling or infinite scroll for very large datasets where traditional pagination becomes cumbersome.


Speed up your responsive apps and websites with fully-featured, ready-to-use open-source admin panel templates—free to use and built for efficiency.


About the Author

Subscribe to our newsletter
Get early information about new products, product updates and blog posts.
What are the three dots `...` in JavaScript do?
What are the three dots `...` in JavaScript do?

How to Merge Objects in JavaScript
How to Merge Objects in JavaScript

How to force a React component to re-render
How to force a React component to re-render

How to migrate CoreUI React Templates to Vite
How to migrate CoreUI React Templates to Vite

Answers by CoreUI Core Team