Dashboard with drill-down

A department overview table that combines expandable rows,conditional row styles for health status, and custom cellrenderers for inline progress bars. Expand any row to drill into a card grid of individual team members — the detail panel is a plain React component, not another table.

Department dashboard

Expand a department row to see individual team member utilization.

Expand any row to see team member utilization.

Department
Headcount
Budget spend
Open tickets
Health
Analytics
3
$410,000 / $420,000
98%
21
critical
Design
4
$195,000 / $480,000
41%
3
good
Engineering
12
$1,240,000 / $1,800,000
69%
8
good
Product
5
$680,000 / $750,000
91%
14
at-risk
Sales
6
$320,000 / $600,000
53%
5
good

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

Conditional row styles

Tint rows based on a derived health field so problems are visible at a glance — no extra column needed:

const conditionalRowStyles: ConditionalStyles<Department>[] = [
  { when: r => r.health === 'critical', style: { backgroundColor: '#fff7f7' } },
  { when: r => r.health === 'at-risk',  style: { backgroundColor: '#fffdf0' } },
];

A card layout in the expander, not a nested table

expandableRowsComponent can render anything — it doesn't have to be anotherDataTable. A grid of team-member cards reads better here than a table-in-a-table. The parent row arrives as data:

function DepartmentDetail({ data: dept }: ExpanderComponentProps<Department>) {
  return (
    <div style={{ padding: '12px 32px', background: '#f9fafb' }}>
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
        {dept.members.map(member => (
          <div key={member.name} style={{ display: 'flex', gap: 12, background: '#fff', border: '1px solid #e5e7eb', borderRadius: 8, padding: '10px 12px' }}>
            <div className="avatar">{initials(member.name)}</div>
            <div style={{ flex: 1 }}>
              <strong>{member.name}</strong>{member.role}
              <UtilBar pct={member.utilization} />
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

<DataTable
  columns={columns}
  data={departments}
  expandableRows
  expandableRowsComponent={DepartmentDetail}
  conditionalRowStyles={conditionalRowStyles}
/>

Expand all / collapse all

expandableRowExpanded re-syncs whenever its result changes, so a single boolean in state — flipped by the "Expand all" button above the demo — expands or collapses every department at once:

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

<button onClick={() => setAllExpanded(v => !v)}>
  {allExpanded ? 'Collapse all' : 'Expand all'}
</button>

<DataTable
  columns={columns}
  data={departments}
  expandableRows
  expandableRowsComponent={DepartmentDetail}
  expandableRowExpanded={() => allExpanded}
/>

Inline progress bars in cells

Use a cell renderer for columns that need richer content than a plain value. The renderer receives the full row, so you can derive bar width and color from the data:

{
  name: 'Budget spend',
  cell: r => {
    const pct = Math.round((r.spent / r.budget) * 100);
    const color = pct >= 95 ? '#ef4444' : pct >= 80 ? '#f59e0b' : '#6366f1';
    return (
      <div style={{ width: '100%' }}>
        <div style={{ fontSize: 11, color: '#6b7280', marginBottom: 3 }}>
          ${r.spent.toLocaleString()} / ${r.budget.toLocaleString()}
        </div>
        <div style={{ height: 4, background: '#f3f4f6', borderRadius: 2, overflow: 'hidden' }}>
          <div style={{ width: `${pct}%`, background: color, height: '100%' }} />
        </div>
      </div>
    );
  },
}