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-inExpanderComponentProps 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. UseexpandableRowDisabled 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 all / collapse all
expandableRowExpanded re-syncs any time its result changes, so a single boolean in state — driven by an "Expand all" button — expands or collapses every row at once. Rows a user has manually toggled since then are left alone until the shared state changes again:
const [allExpanded, setAllExpanded] = useState(false);
<button onClick={() => setAllExpanded(v => !v)}>
{allExpanded ? 'Collapse all' : 'Expand all'}
</button>
<DataTable
columns={columns}
data={data}
expandableRows
expandableRowsComponent={ExpandedRow}
expandableRowExpanded={() => allExpanded}
/>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 hasprefers-reduced-motion set.
<DataTable expandableRows expandableRowsComponent={ExpandedRow} animateRows />;Localization
Override the aria-labels on the expand/collapse button via the localization prop. Use a pre-built locale or supply your own — every key is optional.
// Drop-in locale
import DataTable from 'react-data-table-component';
import { fr } from 'react-data-table-component/locales';
<DataTable columns={columns} data={data} expandableRows expandableRowsComponent={MyExpander} localization={fr} />;// Custom aria-labels only
import type { Localization } from 'react-data-table-component';
const custom: Localization = {
expandable: {
expandRowAriaLabel: 'Ouvrir la ligne',
collapseRowAriaLabel: 'Fermer la ligne',
},
};
<DataTable columns={columns} data={data} expandableRows expandableRowsComponent={MyExpander} localization={custom} />;Prop reference
| Prop | Type | Description |
|---|---|---|
animateRows | boolean | Staggered row entrance + sort animation. |
expandableRows | boolean | Enable expandable row panels |
expandableRowsComponent | React.ComponentType | Component rendered in the expanded panel. Receives { data: T }. |
expandableRowsComponentProps | object | Extra props merged into expandableRowsComponent. |
expandableRowExpanded | (row: T) => boolean | Control which rows start expanded. |
expandableRowDisabled | (row: T) => boolean | Prevent specific rows from being expanded. |
expandableRowsHideExpander | boolean | Hide the expander chevron column (use with expandOnRowClicked). |
expandOnRowClicked | boolean | Toggle expansion by clicking anywhere on the row. |
expandOnRowDoubleClicked | boolean | Toggle expansion by double-clicking anywhere on the row. |
expandableInheritConditionalStyles | boolean | Apply the parent row's conditional styles to the expanded panel. |
expandableIcon | { collapsed, expanded } | Custom expand/collapse icons. |
onRowExpandToggled | (expanded, row) => void | Called when a row is expanded or collapsed. |
localization | Localization | Override every user-visible string and aria-label in the table. Pass a pre-built locale or build your own. |
See it combined with other features in the Dashboard with drill-down recipe and Grouped rows with aggregates.