Skip to content
Harness Design System Harness Design System Harness Design System

Data Table

beta

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 type
type User = {
name: string;
email: string;
status: "active" | "inactive";
};
// Define your columns
const 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
datatrueTData[]
columnstrueColumnDef<TData, unknown>[]
sizefalse'normal''normal' | 'relaxed' | 'compact'
variantfalse'default''default' | 'transparent'
getRowIdfalse(row: TData) => string
paginationPropsfalsePaginationProps
getRowClassNamefalse(row: Row<TData>) => string | undefined
onRowClickfalse(data: TData, index: number) => void
disableHighlightOnHoverfalseboolean
classNamefalsestring
currentSortingfalseSortingState
onSortingChangefalseOnChangeFn<SortingState>
enableRowSelectionfalseboolean
currentRowSelectionfalseRowSelectionState
onRowSelectionChangefalseOnChangeFn<RowSelectionState>
enableExpandingfalseboolean
getRowCanExpandfalse(row: Row<TData>) => boolean
getRowCanSelectfalse(row: Row<TData>) => boolean
currentExpandedfalseExpandedState
onExpandedChangefalseOnChangeFn<ExpandedState>
renderSubComponentfalse(props: { row: Row<TData> }) => React.ReactNode
enableColumnResizingfalseboolean
visibleColumnsfalsestring[]
columnPinningfalseColumnPinningState ({ 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
columnstrueCheckboxOptions[]
visibleColumnstruestring[]
onCheckedChangetrue(columnName: string, checked: boolean) => void
onResetfalse() => void

useColumnFilter Hook

The useColumnFilter hook manages column visibility state with automatic localStorage persistence.

Parameters

Prop
Required
Default
Type
storageKeytruestring
columnstrueCheckboxOptions[]
defaultVisibleColumnsfalsestring[]

Return Value

Prop
Required
Default
Type
visibleColumnsfalsestring[]
toggleColumnfalse(columnName: string, checked: boolean) => void
resetColumnsfalse() => 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 size property 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 transparent variant 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.

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 IDs
const 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 dropdown
const 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}
/>
</>
);
}