Master-Detail
Nest a fully interactive sub-grid, or any custom component, under an expandable parent row.
Master-detail renders a custom component — commonly a nested <Grid> — directly under an expandable parent
row. Enable it with initialState.masterDetailEnabled, size the expanded rows with detailRowHeight, and supply
the content with detailRowRenderer.
import { useCallback, useMemo } from 'react';
import { Grid, type ColumnDef, type VisualRow, type GridApi, type CellRendererProps } from '@witbybit/wit-grid-react';
interface OrderRow {
id: string;
customerName: string;
totalAmount: number;
}
interface OrderItemRow {
id: string;
itemName: string;
price: number;
quantity: number;
}
const DetailToggleRenderer = ({ rowId, api }: CellRendererProps<OrderRow>) => (
<button onClick={() => api.toggleDetailExpanded(rowId)}>{api.isDetailExpanded(rowId) ? '▼' : '▶'}</button>
);
const NestedItemsGrid = ({ visualRow }: { visualRow: VisualRow<OrderRow> }) => {
if (visualRow.kind !== 'detail') return null;
const items: OrderItemRow[] = [
{ id: 'ITM-01', itemName: 'Widget A', price: 25, quantity: 4 },
{ id: 'ITM-02', itemName: 'Widget B', price: 12.5, quantity: 2 },
];
const detailColumns = useMemo<ColumnDef<OrderItemRow>[]>(
() => [
{ field: 'itemName', header: 'Item', width: 220 },
{ field: 'price', header: 'Price', width: 100 },
{ field: 'quantity', header: 'Qty', width: 80 },
],
[]
);
return (
<div style={{ height: '100%', padding: '8px 8px 8px 40px' }}>
<Grid rows={items} columns={detailColumns} getRowId={(row) => row.id} enableNavigation />
</div>
);
};
export function MasterOrdersGrid({ orders }: { orders: OrderRow[] }) {
const masterColumns = useMemo<ColumnDef<OrderRow>[]>(
() => [
{ field: 'toggle', header: '', width: 45, renderer: { kind: 'react', component: DetailToggleRenderer } },
{ field: 'id', header: 'Order ID', width: 120 },
{ field: 'customerName', header: 'Customer', width: 220 },
{ field: 'totalAmount', header: 'Total', width: 120 },
],
[]
);
const detailRowRenderer = useCallback(
({ visualRow }: { visualRow: VisualRow<OrderRow>; api: GridApi<OrderRow> }) => <NestedItemsGrid visualRow={visualRow} />,
[]
);
return (
<Grid
rows={orders}
columns={masterColumns}
getRowId={(row) => row.id}
initialState={{ masterDetailEnabled: true }}
detailRowHeight={220}
detailRowRenderer={detailRowRenderer}
/>
);
}Nested grids are independent instances
The grid rendered inside detailRowRenderer is a completely separate <Grid> instance with its own state,
selection, and API handle — it isn't a view into the parent's data. Fetch or derive its rows from
visualRow.parentId (the master row's id) as needed.
Toggle detail rows programmatically with api.toggleDetailExpanded(rowId), and check state with
api.isDetailExpanded(rowId). detailRowRenderer receives { visualRow, api } where api is the master
grid's GridApi — use it to read sibling row data or coordinate with the parent grid.
Last updated on