Table recipe
Expandable rows
Control expanded rows, skip expansion for selected records, and render arbitrary JSX below each row.
ExpansionControlledDetail panels
Interactive, production component
tsx
import { useMemo, useState } from "react";
import { DataTable, type ColumnSetting } from "../../Datatable";
import { products as initialProducts, type ProductRow } from "./exampleData";
import {
InventoryExpandedPanel,
NON_EXPANDABLE_DEMO_BASE_INDEX,
} from "./InventoryExpandedPanel";
import type { ExampleProps } from "./types";
import "./expandable-detail.css";
const columns: ColumnSetting<ProductRow>[] = [
{ id: "sku", title: "SKU", width: 120 },
{ id: "product", title: "Product", width: 240, pinned: "left" },
{ id: "brand", title: "Brand", width: 120 },
{ id: "status", title: "Status", width: 110, align: "center" },
{ id: "warehouse", title: "Warehouse", width: 110 },
{ id: "stock", title: "Stock", width: 90, align: "right" },
{ id: "owner", title: "Owner", width: 150 },
{ id: "updatedAt", title: "Updated", width: 120 },
];
export default function ExpandableRowsExample({ theme }: ExampleProps) {
const [rows] = useState(() => initialProducts.slice(0, 24));
const [expandedRows, setExpandedRows] = useState<Array<number | ProductRow>>([
rows[0]!,
]);
const featuredCount = useMemo(
() => rows.filter((row) => row.featured).length,
[rows],
);
return (
<div>
<div className="example-demo-toolbar" aria-describedby="expandable-rows-help">
<p id="expandable-rows-help" className="example-demo-toolbar__help">
The +/- gutter opens a measured detail panel below each row. Row{" "}
{NON_EXPANDABLE_DEMO_BASE_INDEX + 1} is intentionally not expandable in this recipe.
</p>
<div className="example-demo-toolbar__actions">
<button type="button" onClick={() => setExpandedRows(rows.slice(0, 2))}>
Open first two
</button>
<button type="button" onClick={() => setExpandedRows([])}>
Collapse all
</button>
<span>{featuredCount} featured rows</span>
</div>
</div>
<DataTable
theme={theme}
title="Expandable inventory rows"
dataSource={rows}
columnSettings={columns}
maxHeight={540}
enableRowSelection
enableColumnResizing
enableColumnSettings
showGlobalSearch
expandedRows={expandedRows}
onExpandedRowsChange={(_, nextRows) => setExpandedRows(nextRows)}
isRowExpandable={(_, baseIndex) => baseIndex !== NON_EXPANDABLE_DEMO_BASE_INDEX}
renderExpandedRow={({ row, collapse }) => (
<InventoryExpandedPanel row={row} collapse={collapse} />
)}
unstyled
tokens={{
"--dt-radius": "6px",
"--dt-expanded-radius": "0px",
"--dt-expanded-bg": theme === "dark" ? "#111827" : "#f8fafc",
"--dt-expanded-border": theme === "dark" ? "#334155" : "#dbe3ef",
}}
classNames={{
root: "expandable-table",
titleBar: "expandable-table__title",
body: "expandable-table__body",
expandedPanel: "expandable-table__panel",
}}
/>
</div>
);
}
Related API