Data Table
The Data Table component provides a powerful way to display and interact with tabular data, featuring row selection, expansion, sorting, and custom cell rendering. Built on TanStack Table, it offers a comprehensive solution for complex data display needs.
Column Filter Demo
Anatomy
<DataTable columns={columns} // Column definitions data={data} // Data to display size="normal" // Size variant getRowId={(row) => row.id} // Function to get unique row ID enableRowSelection // Enable row selection enableExpanding // Enable row expansion renderSubComponent={renderSubComponent} // Render expanded content currentSorting={sorting} // Current sorting state onSortingChange={setSorting} // Sorting change handler pagination={paginationProps} // Pagination configuration/>Usage
import { DataTable } from "@harnessio/ui/components";import { useState } from "react";import type { ColumnDef, SortingState, RowSelectionState, ExpandedState,} from "@tanstack/react-table";
// Define your data typetype User = { name: string; email: string; status: "active" | "inactive";};
// Define your columnsconst columns: ColumnDef<User>[] = [ { accessorKey: "name", header: "Name", enableSorting: true, }, { accessorKey: "email", header: "Email", }, { accessorKey: "status", header: "Status", },];
function MyTable() { // State for sorting, selection, and expansion const [sorting, setSorting] = useState<SortingState>([]); const [rowSelection, setRowSelection] = useState<RowSelectionState>({}); const [expanded, setExpanded] = useState<ExpandedState>({});
// Your data const data = [ { name: "John Doe", email: "john@example.com", status: "active" }, { name: "Jane Smith", email: "jane@example.com", status: "inactive" }, ];
return ( <DataTable columns={columns} data={data} getRowId={(row) => row.email} currentSorting={sorting} onSortingChange={setSorting} enableRowSelection currentRowSelection={rowSelection} onRowSelectionChange={setRowSelection} enableExpanding currentExpanded={expanded} onExpandedChange={setExpanded} renderSubComponent={({ row }) => ( <div className="p-cn-md"> <p>Additional details for {row.original.name}</p> </div> )} /> );}Props
Prop | Required | Default | Type |
|---|---|---|---|
| data | true | TData[] | |
| columns | true | ColumnDef<TData, unknown>[] | |
| size | false | 'normal' | 'normal' | 'relaxed' | 'compact' |
| variant | false | 'default' | 'default' | 'transparent' |
| getRowId | false | (row: TData) => string | |
| paginationProps | false | PaginationProps | |
| getRowClassName | false | (row: Row<TData>) => string | undefined | |
| onRowClick | false | (data: TData, index: number) => void | |
| disableHighlightOnHover | false | boolean | |
| className | false | string | |
| currentSorting | false | SortingState | |
| onSortingChange | false | OnChangeFn<SortingState> | |
| enableRowSelection | false | boolean | |
| currentRowSelection | false | RowSelectionState | |
| onRowSelectionChange | false | OnChangeFn<RowSelectionState> | |
| enableExpanding | false | boolean | |
| getRowCanExpand | false | (row: Row<TData>) => boolean | |
| getRowCanSelect | false | (row: Row<TData>) => boolean | |
| currentExpanded | false | ExpandedState | |
| onExpandedChange | false | OnChangeFn<ExpandedState> | |
| renderSubComponent | false | (props: { row: Row<TData> }) => React.ReactNode | |
| enableColumnResizing | false | boolean | |
| visibleColumns | false | string[] | |
| columnPinning | false | ColumnPinningState ({ left?: string[], right?: string[] }) |
DataTable.ColumnFilter Component
The DataTable.ColumnFilter is a sub-component that provides a dropdown menu for toggling column visibility.
Props
Prop | Required | Default | Type |
|---|---|---|---|
| columns | true | CheckboxOptions[] | |
| visibleColumns | true | string[] | |
| onCheckedChange | true | (columnName: string, checked: boolean) => void | |
| onReset | false | () => void |
useColumnFilter Hook
The useColumnFilter hook manages column visibility state with automatic localStorage persistence.
Parameters
Prop | Required | Default | Type |
|---|---|---|---|
| storageKey | true | string | |
| columns | true | CheckboxOptions[] | |
| defaultVisibleColumns | false | string[] |
Return Value
Prop | Required | Default | Type |
|---|---|---|---|
| visibleColumns | false | string[] | |
| toggleColumn | false | (columnName: string, checked: boolean) => void | |
| resetColumns | false | () => void |
Column Pinning
The DataTable component supports column pinning, allowing you to pin columns to the left or right side of the table. Pinned columns remain visible while scrolling horizontally through the table.
Usage
To enable column pinning, pass a columnPinning prop to the DataTable component with the column IDs you want to pin:
import { DataTable } from "@harnessio/ui/components";import { useState } from "react";import type { ColumnPinningState } from "@tanstack/react-table";
function TableWithPinnedColumns() { const [columnPinning, setColumnPinning] = useState<ColumnPinningState>({ left: ["name"], // Pin 'name' column to the left right: ["status"], // Pin 'status' column to the right });
const columns = [ { id: "name", accessorKey: "name", header: "Name", size: 150, // Optional: Set column width }, { id: "email", accessorKey: "email", header: "Email", size: 200, }, { id: "status", accessorKey: "status", header: "Status", size: 100, }, ];
return ( <DataTable columns={columns} data={data} columnPinning={columnPinning} /> );}Key Features
- Left Pinning: Pin columns to the left side of the table
- Right Pinning: Pin columns to the right side of the table
- Visual Indicators: Pinned columns have subtle shadows to indicate their pinned state
- Sticky Positioning: Pinned columns remain visible during horizontal scrolling
- Multiple Columns: Pin multiple columns to either side
Best Practices
- Pin important identifier columns (like names or IDs) to the left
- Pin action columns or status indicators to the right
- Avoid pinning too many columns as it reduces the scrollable area
- Set explicit column widths using the
sizeproperty for better control
Transparent Variant
The DataTable component supports a transparent variant that removes the background and border styling, making it ideal for embedding tables in cards or within expandable table row or other containers where you want a cleaner, more integrated look.
Usage
import { DataTable, Card } from "@harnessio/ui/components";
function TransparentTable() { const columns = [ { accessorKey: "name", header: "Name" }, { accessorKey: "email", header: "Email" }, ];
const data = [ { name: "John Doe", email: "john@example.com" }, { name: "Jane Smith", email: "jane@example.com" }, ];
return ( <Card.Root> <Card.Title>Users</Card.Title> <Card.Content className="p-0"> <DataTable columns={columns} data={data} variant="transparent" size="compact" /> </Card.Content> </Card.Root> );}Key Features
- No Background: Removes the default table background for a cleaner look
- No Border: Removes outer borders while maintaining internal structure
- Expandable Rows: Perfect for embedding tables within expandable rows of another table.
- Maintains Functionality: All features (sorting, selection, expansion) work normally
Best Practices
- Use
transparentvariant when embedding tables in containers with their own backgrounds - Combine with
size="compact"for a more condensed appearance in cards - Ideal for dashboard widgets and summary tables
Examples
Table with Custom Cell Rendering
import { DataTable, StatusBadge } from "@harnessio/ui/components";
const columns = [ { accessorKey: "name", header: "Name", }, { accessorKey: "status", header: "Status", cell: (info) => ( <StatusBadge theme={info.getValue() === "active" ? "success" : "danger"} size="sm" > {info.getValue()} </StatusBadge> ), },];Table with Conditional Row Selection
<DataTable columns={columns} data={data} enableRowSelection getRowCanSelect={(row) => row.original.status === "active"} currentRowSelection={rowSelection} onRowSelectionChange={setRowSelection}/>Table with Column Visibility Control
The DataTable component provides a built-in column filter dropdown (DataTable.ColumnFilter) and a hook (useColumnFilter) for managing column visibility with localStorage persistence.
Using the Built-in Column Filter (Recommended)
import { DataTable } from "@harnessio/ui/components";import { useColumnFilter } from "@harnessio/ui/hooks";import type { CheckboxOptions } from "@harnessio/ui/components";
// Define columns with enableHiding and unique IDsconst columns = [ { id: "name", accessorKey: "name", header: "Name", enableHiding: true, }, { id: "email", accessorKey: "email", header: "Email", enableHiding: true, }, { id: "status", accessorKey: "status", header: "Status", enableHiding: true, }, { id: "createdAt", accessorKey: "createdAt", header: "Created At", enableHiding: true, },];
// Define column options for the filter dropdownconst COLUMN_OPTIONS: CheckboxOptions[] = [ { label: "Name", value: "name" }, { label: "Email", value: "email" }, { label: "Status", value: "status" }, { label: "Created At", value: "createdAt" },];
function TableWithColumnFilter() { // Use the column filter hook with localStorage persistence const { visibleColumns, toggleColumn, resetColumns } = useColumnFilter({ storageKey: "my-table-columns", // Unique key for localStorage columns: COLUMN_OPTIONS, defaultVisibleColumns: ["name", "email", "status"], // Default visible columns });
const data = [ { name: "John Doe", email: "john@example.com", status: "active", createdAt: "2024-01-15", }, { name: "Jane Smith", email: "jane@example.com", status: "inactive", createdAt: "2024-02-20", }, ];
return ( <> {/* Column filter dropdown */} <DataTable.ColumnFilter columns={COLUMN_OPTIONS} visibleColumns={visibleColumns} onCheckedChange={toggleColumn} onReset={resetColumns} />
{/* DataTable with visible columns */} <DataTable columns={columns} data={data} visibleColumns={visibleColumns} /> </> );}Manual Column Visibility Control
You can also manually control column visibility without using the hook:
import { DataTable } from "@harnessio/ui/components";import { useState } from "react";
function TableWithManualColumnControl() { const [visibleColumns, setVisibleColumns] = useState([ "name", "email", "status", ]);
const handleColumnToggle = (columnId: string, checked: boolean) => { if (checked) { setVisibleColumns([...visibleColumns, columnId]); } else { setVisibleColumns(visibleColumns.filter((id) => id !== columnId)); } };
const handleReset = () => { setVisibleColumns(["name", "email", "status"]); };
return ( <> <DataTable.ColumnFilter columns={COLUMN_OPTIONS} visibleColumns={visibleColumns} onCheckedChange={handleColumnToggle} onReset={handleReset} />
<DataTable columns={columns} data={data} visibleColumns={visibleColumns} /> </> );}