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 — 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.