Grouped rows with aggregates

Row grouping with per-group totals is usually sold as an enterprise grid feature. Here it's a plain array reduce plus two props you already know: expandableRowsto drill into a group's line items, and a column footer for the grand total. The detail panel is a plain list, not a table nested inside a table.

Orders grouped by region

Expand a region to see its individual orders. The Total column sums per-group and grand-totals in the footer.

Region
Orders
Total
West
3
$9,150
East
2
$8,000
Central
4
$7,715

This demo uses Tailwind utility classes for layout. Swap in your own CSS when copying into your project.

Group the flat data yourself

No groupBy prop to learn. Reduce your flat rows into per-group summary objects before they ever reach the table:

const groups: RegionGroup[] = Object.values(
  orders.reduce<Record<string, RegionGroup>>((acc, order) => {
    const g = (acc[order.region] ??= { region: order.region, count: 0, total: 0, orders: [] });
    g.count += 1;
    g.total += order.amount;
    g.orders.push(order);
    return acc;
  }, {}),
);

Drill into a group without nesting another table

Each summary row already carries its own orders, so the expander component needs no extra data fetching. It doesn't need to be a DataTable either: a simple line-item list reads more clearly than a table nested inside a table:

function RegionOrders({ data }: ExpanderComponentProps<RegionGroup>) {
  return (
    <div style={{ padding: '10px 32px' }}>
      {data.orders.map(order => (
        <div key={order.id} style={{ display: 'flex', justifyContent: 'space-between', background: '#fff', border: '1px solid #e5e7eb', borderRadius: 6, padding: '8px 12px', marginBottom: 6 }}>
          <span>{order.customer}</span>
          <strong>${order.amount.toLocaleString()}</strong>
        </div>
      ))}
    </div>
  );
}

<DataTable
  columns={columns}
  data={groups}
  keyField="region"
  expandableRows
  expandableRowsComponent={RegionOrders}
/>

Expand all / collapse all

Same pattern as any other expandable table: one boolean in state, flipped by the button above the demo, expands or collapses every region at once:

const [allExpanded, setAllExpanded] = useState(false);

<DataTable
  columns={columns}
  data={groups}
  keyField="region"
  expandableRows
  expandableRowsComponent={RegionOrders}
  expandableRowExpanded={() => allExpanded}
/>

Per-group and grand totals with footer

The same footer function that produces a table-wide total also works on grouped data, since it receives whatever rows are currently visible:

{
  id: 'total',
  name: 'Total',
  selector: r => r.total,
  format: r => `$${r.total.toLocaleString()}`,
  footer: (rows: RegionGroup[]) => `$${rows.reduce((sum, r) => sum + r.total, 0).toLocaleString()}`,
}

See Footer for more on static labels vs. computed aggregates.