Version: 1.0
Last Updated: June 6, 2025
Status: Production Ready
The DataTable component, located at src/components/DataTable.tsx, is a reusable and flexible table component designed to display, sort, filter, and paginate data. It is built using the @tanstack/react-table library (v8) and integrates with UI primitives from src/components/ui/.
To provide a consistent and feature-rich way to render tabular data across the ClaimBot application. It supports:
- Dynamic column definitions.
- Client-side pagination.
- Client-side sorting by column.
- Client-side global filtering (search across all columns).
- Client-side column-specific filtering (if column definitions include filter UIs).
The component accepts the following props, defined by the DataTableProps<TData, TValue> interface:
- Type: An array of
ColumnDefobjects from@tanstack/react-table. - Description: Defines the structure and behavior of each column in the table. Each
ColumnDefspecifies how to access data for the column (accessorKeyoraccessorFn), what to render in the header (header), and what to render in the cell (cell). Headers and cells can be simple strings or custom React components. - Example
ColumnDef:import { ColumnDef } from "@tanstack/react-table"; // Assuming TData is your data row type, e.g., interface MyData { id: string; name: string; email: string; } export const columns: ColumnDef<MyData>[] = [ { accessorKey: "name", header: "Name", }, { accessorKey: "email", header: "Email Address", }, { id: "actions", header: "Actions", cell: ({ row }) => { const item = row.original; return ( <Button onClick={() => console.log("Action for:", item.id)}> View Details </Button> ); }, }, ];
- Type: An array of data objects, where
TDatais the type of each row's data. - Description: The actual data to be displayed in the table.
- Type:
string - Description: An externally controlled global filter string. If provided, the table will filter rows based on this string. The component also has an internal global filter input if this prop is not used.
- Usage: Useful when you want a search input outside the
DataTablecomponent to control its filtering.
- Type: React state setter function.
- Description: If
globalFilteris provided and managed externally, this setter function should also be provided to allow theDataTable's internal mechanisms (if any were to be added for clearing filters from within) or future enhancements to update the external global filter state. Currently, ifsetGlobalFilteris not provided, the component renders its own global search input that uses an internal state for filtering. IfsetGlobalFilteris provided, the internal search input is hidden, assuming the parent component manages the search input UI.
"use client";
import { DataTable } from "@/components/DataTable";
import { ColumnDef } from "@tanstack/react-table";
import React from "react";
interface Payment {
id: string;
amount: number;
status: "pending" | "processing" | "success" | "failed";
email: string;
}
const payments: Payment[] = [
{ id: "1", amount: 100, status: "success", email: "ken99@yahoo.com" },
{ id: "2", amount: 150, status: "pending", email: "alice@example.com" },
// ...more data
];
export const columns: ColumnDef<Payment>[] = [
{ accessorKey: "email", header: "Email" },
{ accessorKey: "amount", header: "Amount" },
{ accessorKey: "status", header: "Status" },
];
export default function PaymentsPage() {
const [searchTerm, setSearchTerm] = React.useState("");
return (
<div>
<Input
placeholder="Search all payments..."
value={searchTerm}
onChange={(event) => setSearchTerm(event.target.value)}
className="max-w-sm mb-4"
/>
<DataTable
columns={columns}
data={payments}
globalFilter={searchTerm}
setGlobalFilter={setSearchTerm} // Provide if using external search input
/>
</div>
);
}- Table Instance: Uses
useReactTablehook from@tanstack/react-tableto create and manage the table instance. - Core Models: Implements
getCoreRowModel,getPaginationRowModel,getSortedRowModel, andgetFilteredRowModel. - State Management:
sorting: Manages the current sorting state (SortingState).columnFilters: Manages column-specific filter states (ColumnFiltersState).internalGlobalFilter: Manages the global search term ifglobalFilterandsetGlobalFilterprops are not provided.
- Rendering:
- Global Filter UI: If
setGlobalFilterprop is not provided, anInputfield is rendered above the table for global searching. This input updates theinternalGlobalFilterstate. - Pagination Controls: Renders "Previous" and "Next" buttons (
Buttoncomponent) for navigating pages. - No Results: Displays a "No results." message if the table data is empty or filtered to an empty set.
- Relies on global styles and Tailwind CSS utility classes.
- Uses components from
src/components/ui/which are pre-styled (e.g., border for the table container).
@tanstack/react-tablereact- UI primitive components from
src/components/ui/(e.g.,table,button,input).
- Columns: The primary way to customize the table is through the
columnsprop. Complex rendering, custom sorting logic, and column-specific filtering UIs can be defined withinColumnDefobjects. - Filtering: While global filtering is built-in, column-specific filter UIs need to be implemented as part of the
headerrendering inColumnDefand linked to the table'scolumn.getFilterValue()andcolumn.setFilterValue()methods. - Styling: Can be further styled using Tailwind CSS classes passed via
classNameprops to theDataTableor by customizing the underlyingsrc/components/ui/components.
{
accessorKey: "amount",
header: ({ column }) => {
return (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
>
Amount
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
)
},
}{
accessorKey: "status",
header: "Status",
cell: ({ row }) => {
const status = row.getValue("status") as string;
return <StatusBadge status={status} />;
},
}{
id: "actions",
header: "Actions",
cell: ({ row }) => {
const item = row.original;
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="h-8 w-8 p-0">
<MoreHorizontal className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={() => handleEdit(item.id)}>
Edit
</DropdownMenuItem>
<DropdownMenuItem onClick={() => handleDelete(item.id)}>
Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
},
}- Client-Side Operations: All filtering, sorting, and pagination are performed client-side. For large datasets (>1000 rows), consider implementing server-side operations.
- Memoization: Consider memoizing column definitions and data to prevent unnecessary re-renders.
- Virtual Scrolling: For very large datasets, consider integrating with virtual scrolling libraries.
const claimsColumns: ColumnDef<Claim>[] = [
{ accessorKey: "date", header: "Date" },
{ accessorKey: "description", header: "Description" },
{ accessorKey: "totalClaim", header: "Amount" },
{ accessorKey: "status", header: "Status", cell: ({ row }) => <StatusBadge status={row.getValue("status")} /> },
];const usersColumns: ColumnDef<User>[] = [
{ accessorKey: "name", header: "Name" },
{ accessorKey: "email", header: "Email" },
{ accessorKey: "department", header: "Department" },
{ accessorKey: "roles", header: "Roles", cell: ({ row }) => row.getValue("roles").join(", ") },
];- Columns not rendering: Ensure
accessorKeymatches the data property names exactly. - Sorting not working: Check that the data type is sortable (strings, numbers, dates).
- Filtering not working: Verify that
globalFilterstate is properly managed. - Performance issues: Consider memoizing columns and data, or implementing server-side operations.
// Add this to debug table state
console.log("Table state:", table.getState());
console.log("Filtered rows:", table.getFilteredRowModel().rows.length);
console.log("Current page:", table.getState().pagination.pageIndex);- UI Components - Base UI primitives used by DataTable
- System Architecture - Overall component architecture
- API Reference - Data sources for tables
Next Steps: Review the LocationAutocomplete Component for location input functionality.