Back to Blog
React14 min read

TanStack Table Implementation in React: Complete Guide

TanStack Table (formerly React Table) is a powerful headless table library that provides sorting, filtering, pagination, and many other features. In this guide, we'll build a complete data table component for an inventory management system.

TanStack Table (formerly React Table) is a powerful headless table library that provides sorting, filtering, pagination, and many other features. In this guide, we'll build a complete data table component for an inventory management system.

Installation

npm install @tanstack/react-table

Basic Table Setup

Let's create a reusable table component:

import {
  useReactTable,
  getCoreRowModel,
  getFilteredRowModel,
  getSortedRowModel,
  getPaginationRowModel,
  flexRender,
  type ColumnDef,
} from "@tanstack/react-table";

function TanstackTable({ columns, data, isLoading, globalFilter }) {
  const [rowSelection, setRowSelection] = useState({});
  const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: 10 });
  const [columnFilters, setColumnFilters] = useState([]);

  const table = useReactTable({
    data,
    columns,
    state: {
      columnFilters,
      globalFilter,
      pagination,
      rowSelection,
    },
    getCoreRowModel: getCoreRowModel(),
    getFilteredRowModel: getFilteredRowModel(),
    getSortedRowModel: getSortedRowModel(),
    getPaginationRowModel: getPaginationRowModel(),
    onColumnFiltersChange: setColumnFilters,
    onGlobalFilterChange: setGlobalFilter,
    onPaginationChange: setPagination,
    onRowSelectionChange: setRowSelection,
    enableRowSelection: true,
    enableGlobalFilter: true,
  });

  if (isLoading) return <div>Loading...</div>;

  return (
    <div className="space-y-4">
      <table className="table" style={{ width: `${table.getTotalSize()}px` }}>
        <thead>
          {table.getHeaderGroups().map((headerGroup) => (
            <tr key={headerGroup.id}>
              {headerGroup.headers.map((header) => (
                <th key={header.id}>
                  {flexRender(header.column.columnDef.header, header.getContext())}
                  {header.column.getCanSort() && (
                    <span onClick={header.column.getToggleSortingHandler()}>
                      {header.column.getIsSorted() === "asc" ? " ▲" : 
                       header.column.getIsSorted() === "desc" ? " ▼" : " ↕"}
                    </span>
                  )}
                </th>
              ))}
            </tr>
          ))}
        </thead>
        <tbody>
          {table.getRowModel().rows.map((row) => (
            <tr key={row.id} className={row.getIsSelected() ? "selected" : undefined}>
              {row.getVisibleCells().map((cell) => (
                <td key={cell.id}>
                  {flexRender(cell.column.columnDef.cell, cell.getContext())}
                </td>
              ))}
            </tr>
          ))}
        </tbody>
      </table>

      <div className="pagination-container">
        <button onClick={() => table.firstPage()} disabled={!table.getCanPreviousPage()}>
          {"<<"}
        </button>
        <button onClick={() => table.previousPage()} disabled={!table.getCanPreviousPage()}>
          {"<"}
        </button>
        <span>
          Page {table.getState().pagination.pageIndex + 1} of {table.getPageCount()}
        </span>
        <button onClick={() => table.nextPage()} disabled={!table.getCanNextPage()}>
          {">"}
        </button>
        <button onClick={() => table.lastPage()} disabled={!table.getCanNextPage()}>
          {">>"}
        </button>
      </div>
    </div>
  );
}

Defining Columns

Here's how to define columns for a products table:

import type { ColumnDef } from "@tanstack/react-table";
import type { Product } from "../../types";

const columns: ColumnDef<Product>[] = [
  {
    accessorKey: "name",
    header: "Product Name",
    cell: (row) => row.getValue(),
  },
  {
    accessorKey: "sku",
    header: "SKU",
    cell: (row) => row.getValue(),
  },
  {
    accessorKey: "categoryName",
    header: "Category",
    cell: (row) => row.getValue() || "N/A",
  },
  {
    accessorKey: "price",
    header: "Price",
    cell: (row) => `$${(row.getValue() as number).toFixed(2)}`,
  },
  {
    accessorKey: "stock",
    header: "Stock",
    cell: (row) => {
      const stock = row.getValue() as number;
      const minStock = row.row.original.minStock || 0;
      return (
        <span className={stock <= minStock ? "text-red-600 font-semibold" : ""}>
          {stock}
        </span>
      );
    },
  },
];

Using the Table Component

import TanstackTable from "../../components/TanstackTable/TanstackTable";
import { useGetProductsQuery } from "../../state/products/productSlice";

function Products() {
  const [globalFilter, setGlobalFilter] = useState("");
  const { data, isLoading, isError } = useGetProductsQuery({});
  
  const products = data?.data || [];

  return (
    <div>
      <input
        value={globalFilter ?? ""}
        onChange={(e) => setGlobalFilter(String(e.target.value))}
        placeholder="Search products..."
        className="px-4 py-2 border rounded-lg"
      />
      
      <TanstackTable
        columns={columns}
        data={products}
        isLoading={isLoading}
        isError={isError}
        globalFilter={globalFilter}
        emptyMessage="No products found."
      />
    </div>
  );
}

Row Selection

Adding row selection with checkboxes:

{
  id: "select",
  header: ({ table }) => (
    <IndeterminateCheckbox
      checked={table.getIsAllRowsSelected()}
      indeterminate={table.getIsSomeRowsSelected()}
      onChange={table.getToggleAllRowsSelectedHandler()}
    />
  ),
  cell: ({ row }) => (
    <IndeterminateCheckbox
      checked={row.getIsSelected()}
      disabled={!row.getCanSelect()}
      indeterminate={row.getIsSomeSelected()}
      onChange={row.getToggleSelectedHandler()}
    />
  ),
  size: 50,
},

Conclusion

TanStack Table provides a flexible, performant solution for building complex data tables in React. With features like sorting, filtering, pagination, and row selection, it's perfect for inventory management systems and data-heavy applications. The headless design allows for complete customization while providing powerful features out of the box.