Expandable rows
Pass expandableRows and an expandableRowsComponent to render a
detail panel below each row when the user clicks the expander button.
Expandable detail panel
Click the ▶ button to expand a row. Toggle 'Animate expand' or disable 'Restricted' rows with the checkboxes.
import { useState } from 'react';
import DataTable, { type TableColumn } from 'react-data-table-component';
interface Employee {
id: number;
name: string;
role: string;
department: string;
salary: number;
bio: string;
locked: boolean;
}
const data: Employee[] = [
{
id: 1, name: 'Aria Chen', role: 'Engineering Lead', department: 'Engineering',
salary: 155000, locked: false,
bio: 'Aria leads the platform team, focusing on reliability and developer experience.',
},
{
id: 2, name: 'Marcus Webb', role: 'Product Manager', department: 'Product',
salary: 132000, locked: true,
bio: 'Marcus drives roadmap strategy across three product lines.',
},
{
id: 3, name: 'Priya Kapoor', role: 'Senior Designer', department: 'Design',
salary: 118000, locked: false,
bio: 'Priya leads the design system and owns the mobile experience.',
},
{
id: 4, name: 'Jordan Ellis', role: 'Data Scientist', department: 'Analytics',
salary: 143000, locked: true,
bio: 'Jordan builds ML pipelines for churn prediction and revenue forecasting.',
},
];
const columns: TableColumn<Employee>[] = [
{ name: 'Name', selector: r => r.name, sortable: true },
{ name: 'Role', selector: r => r.role },
{ name: 'Department', selector: r => r.department },
{ name: 'Salary', selector: r => r.salary, format: r => `$${r.salary.toLocaleString()}`, right: true },
];
// The expandable component receives the row as the `data` prop
const ExpandedRow = ({ data: row }: { data: Employee }) => (
<div style={{ padding: '16px 40px', background: '#f9fafb', borderBottom: '1px solid #e5e7eb' }}>
<p style={{ fontWeight: 600, marginBottom: 4 }}>{row.name}</p>
<p style={{ color: '#6b7280', margin: 0 }}>{row.bio}</p>
</div>
);
export default function App() {
const [animate, setAnimate] = useState(true);
const [disableLocked, setDisableLocked] = useState(false);
return (
<div>
<label>
<input type="checkbox" checked={animate} onChange={e => setAnimate(e.target.checked)} />
{' '}Animate expand
</label>
<label>
<input type="checkbox" checked={disableLocked} onChange={e => setDisableLocked(e.target.checked)} />
{' '}Disable "Restricted" rows
</label>
<DataTable
columns={columns}
data={data}
expandableRows
expandableRowsComponent={ExpandedRow}
expandableRowDisabled={disableLocked ? (r => r.locked) : undefined}
animateRows={animate}
highlightOnHover
/>
</div>
);
} The expander component
The component you pass to expandableRowsComponent receives one prop,
data, which is the full row object. Type it with the built-in
ExpanderComponentProps helper:
import DataTable, { type TableColumn, type ExpanderComponentProps } from 'react-data-table-component';
interface Employee {
id: number;
name: string;
bio: string;
}
// Receives { data: Employee }
const ExpandedRow = ({ data: row }: ExpanderComponentProps<Employee>) => (
<div style={{ padding: '16px 40px', background: '#f9fafb' }}>
<strong>{row.name}</strong>
<p>{row.bio}</p>
</div>
);
export default function App() {
return (
<DataTable
columns={columns}
data={data}
expandableRows
expandableRowsComponent={ExpandedRow}
/>
);
} Passing extra props to the expander
Use expandableRowsComponentProps to inject additional static props into every
expander instance. These are merged with the data prop.
const DetailPanel = ({ data: row, onDelete }: { data: Employee; onDelete: (id: number) => void }) => (
<div style={{ padding: '16px 40px' }}>
<p>{row.bio}</p>
<button onClick={() => onDelete(row.id)}>Delete</button>
</div>
);
<DataTable
columns={columns}
data={data}
expandableRows
expandableRowsComponent={DetailPanel}
expandableRowsComponentProps={{ onDelete: handleDelete }}
/> Controlling which rows are expanded
Use expandableRowExpanded to pre-expand rows on mount. Use
expandableRowDisabled to prevent specific rows from being expandable. Their
expander icon is hidden and clicks are ignored. Toggle "Disable Restricted rows" in the
demo above to see this in action.
// Pre-expand specific rows on mount
<DataTable
expandableRows
expandableRowsComponent={ExpandedRow}
expandableRowExpanded={row => row.featured === true}
/>
// Prevent certain rows from being expanded
<DataTable
expandableRows
expandableRowsComponent={ExpandedRow}
expandableRowDisabled={row => row.locked === true}
/> Expand on row click
Instead of requiring the user to click the expander icon, you can expand/collapse on a full row click or double-click.
// Single click anywhere on the row expands it
<DataTable
expandableRows
expandableRowsComponent={ExpandedRow}
expandOnRowClicked
/>
// Double-click to expand
<DataTable
expandableRows
expandableRowsComponent={ExpandedRow}
expandOnRowDoubleClicked
/>
// Hide the expander icon entirely (click-to-expand still works)
<DataTable
expandableRows
expandableRowsComponent={ExpandedRow}
expandOnRowClicked
expandableRowsHideExpander
/> Reacting to expand / collapse
<DataTable
expandableRows
expandableRowsComponent={ExpandedRow}
onRowExpandToggled={(expanded: boolean, row: Employee) => {
console.log(expanded ? 'opened' : 'closed', row.name);
}}
/> Custom expander icons
Override the default collapse/expand icons with any React node via the expandableIcon prop:
<DataTable
expandableRows
expandableRowsComponent={ExpandedRow}
expandableIcon={{
collapsed: <span>▶</span>,
expanded: <span>▼</span>,
}}
/> Animation
Add animateRows to get a smooth expand-in animation on the detail panel.
The animation is automatically disabled when the user's OS has
prefers-reduced-motion set.
<DataTable
expandableRows
expandableRowsComponent={ExpandedRow}
animateRows
/> All expandable props
| Prop | Type | Description |
|---|---|---|
expandableRows | boolean | Enable expandable rows |
expandableRowsComponent | ComponentType<{ data: T }> | Component rendered inside each expanded row |
expandableRowsComponentProps | Record<string, unknown> | Extra props injected into every expander instance |
expandableRowExpanded | (row: T) => boolean | Pre-expand rows matching this predicate |
expandableRowDisabled | (row: T) => boolean | Prevent rows matching this predicate from being expanded |
expandOnRowClicked | boolean | Expand/collapse on row click |
expandOnRowDoubleClicked | boolean | Expand/collapse on row double-click |
expandableRowsHideExpander | boolean | Hide the expander icon column |
expandableIcon | { collapsed: ReactNode; expanded: ReactNode } | Custom expand/collapse icons |
expandableInheritConditionalStyles | boolean | Apply the parent row's conditionalRowStyles to the expander row |
onRowExpandToggled | (expanded: boolean, row: T) => void | Fired when a row is expanded or collapsed |
animateRows | boolean | Animate expand/collapse (respects prefers-reduced-motion) |