How to Build a React Data Table with Server-Side Pagination, Sorting and Search

by Vincy. Last modified on October 29th, 2025.

The React TanStack table is a great tool for displaying an elegant view of data table. It provides enriched features to build list with impressive user experience.

This tutorial uses Server-side data fetching and rendering, searching and sorting features.

A shopping cart product table is created in the backend. It has basic details to be listed in a tabular form. The React app connects to the PHP API to connect this database to fetch the rows.

The React TanStack data table shows these data rows in the front end as shown below.

react data table db rows

Setting up the React TanStack table example code

This command is used to install the React TanStack data table library.

npm install @tanstack/react-table

This will add the following library reference to the application’s package config.

npm install @tanstack/react-table "dependencies": {
    "@tanstack/react-table": "^8.21.3",
    ...
    ...
  },

If you are going to run this example in your environment, follow the below steps to set up. It has all the build required for the React TanStack data table rendering.

  1. Download and extract the React project bundle.
  2. Go to the React project directory by cd react-data-table-server-side-pagination-sorting-search
  3. Run npm install to create node_modules.
  4. Copy /api/react-product-table-api/ to your PHP root and configure in React JSX.
  5. Create database db_shopping_cart and import sql/database.sql

Then, start the dev server by using npm run dev.

How to initiate React TanStack data table?

This is to initiate React TabStack library using useReactTable. It adds the raw data reference to the TanStack library row column object to render data.

Part of TanStack.jsx

import { useEffect, useState, useCallback } from "react";
import axios from "axios";
import {
    useReactTable,
    getCoreRowModel,
    getSortedRowModel,
    flexRender,
} from "@tanstack/react-table";
import "./TanStack.css";

const TanStack = () => {
    const [data, setData] = useState([]);
    const [search, setSearch] = useState("");
    const [page, setPage] = useState(1);
    const [limit, setLimit] = useState(5);
    const [total, setTotal] = useState(0);
    const [sortBy, setSortBy] = useState("id");
    const [order, setOrder] = useState("asc");

    ...
    //Fetch Data
    ...

    useEffect(() => {
        fetchData();
    }, [fetchData]);

    const columns = [
        {
            header: "S.No",
            accessorKey: "sno",
            cell: (info) => (page - 1) * limit + info.row.index + 1,
        },
        {
            header: "Title",
            accessorKey: "title",
            cell: (info) => info.getValue(),
        },
        {
            header: "Price",
            accessorKey: "price",
            cell: (info) => `$${info.getValue()}`,
        },
        {
            header: "Category",
            accessorKey: "category",
            cell: (info) => info.getValue(),
        },
    ];

    const table = useReactTable({
        data,
        columns,
        getCoreRowModel: getCoreRowModel(),
        getSortedRowModel: getSortedRowModel(),
    });
    ...
    ...
}

Designing the table layout and components

There are three major UI parts in this React data table example.

  1. The tabular view.
  2. Search bar.
  3. Footer pagination component.

1. The tabular view

react tanstack table filter

Table header - Part of TanStack.jsx

<th key={header.id}
    className={alignClass}
    onClick={() =>
        key !== "sno" && handleSort(key)
    }
    style={{
        cursor: key === "sno" ? "default" : "pointer",
    }}
>
{flexRender(header.column.columnDef.header, header.getContext())}

{key !== "sno" &&
    sortBy === key &&
    (order === "asc" ? (
        <img
            src="/icons/up.svg"
            alt="asc"
            style={{
                width: "14px",
                height: "14px",
                marginLeft: "5px",
                verticalAlign: "middle",
            }}
        />
    ) : (
        <img
            src="/icons/down.svg"
            alt="desc"
            style={{
                width: "14px",
                height: "14px",
                marginLeft: "5px",
                verticalAlign: "middle",
            }}
        />
    ))}
</th>

This section has the TanStack script for iterating the table row array using table.gerRowModel() reference.

The flexRender function loads the table cell data in a row.

Table body rows in TanStack.jsx

<tbody>
   {table.getRowModel().rows.length > 0 ? (
      table.getRowModel().rows.map((row) => (
            <tr key={row.id}>
               {row.getVisibleCells().map((cell) => {
                  const key = cell.column.columnDef.accessorKey;
                  const alignClass =
                        key === "price" ? "price" :
                           key === "sno" ? "center" : "";

                  return (
                        <td key={cell.id} className={alignClass}>
                           {flexRender(cell.column.columnDef.cell, cell.getContext())}
                        </td>
                  );
               })}
            </tr>
      ))
   ) : (
      <tr>
            <td colSpan={columns.length}>No data found</td>
      </tr>
   )}
</tbody>

2. Search Container

It is a usual HTML input that sets the search keyword on-change. When the search is on, it resets the current-page value back to 1.

Search input JSX in TanStack.jsx

<div className="search-container">
    <img src="/icons/search.svg" alt="search" className="search-icon" />
    <input
        type="text"
        placeholder="Search by title..."
        value={search}
        onChange={(e) => {
            setSearch(e.target.value);
            setPage(1);
        }}
        className="search-input"
    />
</div>

3. Pagination

This component returns JSX with numbered buttons with a previous-next pagination link to move the row pointer back and forth.

Pagination JSX

<div className="pagination-container">
    <button
        className="pagination-btn"
        onClick={() => setPage((prev) => Math.max(prev - 1, 1))}
        disabled={page === 1}
    >
        <img
            src="/icons/back.svg"
            alt="Previous"
            className="pagination-icon"
        />
        Prev
    </button>

    <span className="pagination-info">
        Page {page} of {totalPages || 1}
    </span>

    <button
        className="pagination-btn"
        onClick={() => setPage((prev) => (prev < totalPages ? prev + 1 : prev))}
        disabled={page >= totalPages}
    >
        Next
        <img
            src="/icons/forward.svg"
            alt="Next"
            className="pagination-icon"
        />
    </button>

    <select
        value={limit}
        onChange={(e) => {
            setLimit(Number(e.target.value));
            setPage(1);
        }}
        className="pagination-select"
    >
        {[5, 10, 20, 50].map((num) => (
            <option key={num} value={num}>
                {num} / page
            </option>
        ))}
    </select>
</div>

paginated results with react data table

Fetching Data from the Server API

This is the place to configure the server-side API URL to connect to the database for displaying dynamic grid. It sets the data array for the TanStack initiation.

This example has a single endpoint that receives all the search, sort and pagination inputs. The following React useCallback() passes these parameters to the API.

The React callback hook sets the raw data for the TanStack’s getCoreRowModel block to build a table row instance.

Part of TanStack.jsx

const fetchData = useCallback(async () => {
    try {
        const res = await axios.get("http://localhost/react-product-table-api/products.php", {
            params: { search, page, limit, sortBy, order },
        });

        const fetchedData = res.data.data || res.data.products || [];
        setData(fetchedData);
        setTotal(res.data.total || 0);
    } catch (err) {
        console.error("Error fetching data:", err);
    }
}, [search, page, limit, sortBy, order]);

Below PHP script shows how to process a request from the allowed React origin. This PHP service prepares the SELECT query based on the search keyword, current page and sorting column & order passed from the React frontend.

react-product-table-api/products.php

<?php
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json");
require_once "db.php";

$page = $_GET['page'] ?? 1;
$limit = $_GET['limit'] ?? 5;
$search = $_GET['search'] ?? '';
$sortBy = $_GET['sortBy'] ?? 'id';
$order = $_GET['order'] ?? 'asc';
$offset = ($page - 1) * $limit;


$allowedSort = ['id', 'title', 'price', 'category'];
$allowedOrder = ['asc', 'desc'];

if (!in_array($sortBy, $allowedSort)) $sortBy = 'id';
if (!in_array(strtolower($order), $allowedOrder)) $order = 'asc';

$sql = "SELECT * FROM products WHERE title LIKE ? ORDER BY $sortBy $order LIMIT ?, ?";
$stmt = $conn->prepare($sql);
$searchParam = "%$search%";
$stmt->bind_param("sii", $searchParam, $offset, $limit);
$stmt->execute();
$result = $stmt->get_result();

$data = [];
while ($row = $result->fetch_assoc()) {
    $data[] = $row;
}

$countSql = "SELECT COUNT(*) AS total FROM products WHERE title LIKE ?";
$countStmt = $conn->prepare($countSql);
$countStmt->bind_param("s", $searchParam);
$countStmt->execute();
$countResult = $countStmt->get_result()->fetch_assoc();

echo json_encode([
    "products" => $data,
    "total" => $countResult["total"]
]);
?>

Conclusion

This example built a front end React data table with search, sort and pagination features. The TanStack table library provides more features apart these three features. For example, it has column ordering, row selection, resizing and more. But, we covered the prime features of this library that are need in majority of the frontend tables.

References

  1. TanStack table installation guidelines
  2. Data table design patterns

Download

Vincy
Written by Vincy, a web developer with 15+ years of experience and a Masters degree in Computer Science. She specializes in building modern, lightweight websites using PHP, JavaScript, React, and related technologies. Phppot helps you in mastering web development through over a decade of publishing quality tutorials.

Leave a Reply

Your email address will not be published. Required fields are marked *

↑ Back to Top

Share this page