Skip to main content

DataTable - Expansion

Displays a matrix of information with columns, rows, and information that can operate dynamically.

Submit feedback
github
import { DataTable } from '@uhg-abyss/web/ui/DataTable';
import { useDataTable } from '@uhg-abyss/web/hooks/useDataTable';

There are three different types of expansion available:

  • 'subRows': This mode allows you to expand rows to show additional, nested sub-rows. This is useful for displaying hierarchical data or related information.
  • 'subComponent': This mode allows you to expand rows to show a custom sub-component. This is useful for displaying detailed information or custom content related to the row.
  • 'grouping': This mode allows you to expand rows to show grouped data. This is useful for displaying aggregated information.

Note: The expandColumnConfig.expandMode property only needs to be set for 'subRows' and 'subComponent'. Grouping is enabled and managed via tableConfig.

Sub-rows

Sub-row expansion allows rows to expand to show additional, nested sub-rows. To enable it, set expandColumnConfig.expandMode to 'subRows'.

const dataTableProps = useDataTable({
// ...
expandColumnConfig: {
expandMode: 'subRows',
},
// ...
});

Disclaimer: Features such as row drag-and-drop will not be compatible with sub-row expansion.

Programmatically expanding sub-rows

To manage the expansion state, provide a function to the tableConfig.onExpandedChange property and set the state.expanded property; we recommend using useState for this, as shown below.

const [expanded, setExpanded] = useState({});
const dataTableProps = useDataTable({
// ...
expandColumnConfig: {
expandMode: 'subRows',
},
tableConfig: {
onExpandedChange: setExpanded,
state: {
expanded,
},
},
// ...
});

Filtering sub-rows

To enable filtering on sub-rows, set up filtering as normal and set tableConfig.filterFromLeafRows to true.

const dataTableProps = useDataTable({
// ...
tableConfig: {
filterFromLeafRows: true,
},
// ...
});

Paginating sub-rows

By default, expanded rows are considered separate for the purposes of pagination; that is, if a row is expanded to show enough rows that there are more than the current page size, the other rows will be pushed to the next page. To prevent this behavior and to always show sub-rows on the same page as their parent, set tableConfig.paginateExpandedRows to false.

const dataTableProps = useDataTable({
// ...
tableConfig: {
paginateExpandedRows: false,
},
// ...
});

Sub-component

Sub-component expansion allows rows to expand to show a custom sub-component. To enable it, set expandColumnConfig.expandMode to 'subComponent' and provide the custom sub-component to expandColumnConfig.renderSubComponent. The renderSubComponent function receives the row object as a prop, which contains the data for the row being expanded.

The sub-component also requires a fixed height. Use the expandColumnConfig.subComponentHeight property to set this height. In the example below, the component is 50px tall, but the subComponentHeight is 100px; the extra space is used for padding.

const renderSubComponent = ({ row }) => {
const { col1, col2, col3, col4 } = row.original;
const content = `On ${col2}, "${col1}" had ${col3} instances and was marked as ${col4}.`;
return (
<div style={{ height: '100%', width: '100%', alignContent: 'center' }}>
{content}
</div>
);
};
const dataTableProps = useDataTable({
// ...
expandColumnConfig: {
expandMode: 'subComponent',
renderSubComponent,
subComponentHeight: 100,
},
// ...
});

For accessibility purposes, you will also need to define which column best labels the contents of its row. This changes cells in that column from <th> to <th scope="row"> to help screen reader users more clearly understand data in that row.

const dataTableProps = useDataTable({
// ...
initialColumns: [
{
header: 'Name',
accessorKey: 'name',
meta: {
isRowHeader: true,
},
},
// ...
],
// ...
});

Programmatically expanding sub-components

As with sub-rows, to manage the expansion state, provide a function to the tableConfig.onExpandedChange property and set the state.expanded property; we recommend using useState for this, as shown below.

const [expanded, setExpanded] = useState({});
const dataTableProps = useDataTable({
// ...
expandColumnConfig: {
expandMode: 'subComponent',
renderSubComponent,
subComponentHeight: 100,
},
tableConfig: {
onExpandedChange: setExpanded,
state: {
expanded,
},
},
// ...
});

Grouping

Grouping expansion allows rows to be grouped by column values and then expanded to show all rows in the group. It is disabled by default. To enable grouping for all columns, set tableConfig.enableGrouping to true.

const dataTableProps = useDataTable({
// ...
tableConfig: {
enableGrouping: true,
},
// ...
});

The enableGrouping property can also be provided to individual columns for more granular adjustment.

{
header: 'Column 1',
accessorKey: 'col1',
enableGrouping: true,
}

To manage the grouping state, provide a function to the tableConfig.onGroupingChange property and set the state.grouping property; we recommend using useState for this, as shown below.

const [grouping, setGrouping] = useState([]);
const dataTableProps = useDataTable({
// ...
tableConfig: {
enableGrouping: true,
onGroupingChange: setGrouping,
state: {
grouping,
},
},
// ...
});

Please see the TanStack Table grouping docs for more details and configuration options.

Note: As noted in the TanStack docs above, "There are not currently many easy ways to do server-side grouping with TanStack Table." For this reason, we do not allow server-side grouping in DataTable and grouping cannot be used with server-side operations.

Disclaimer: Features such as row drag-and-drop will not be compatible with grouping.

Paginating grouped rows

By default, expanded rows are considered separate for the purposes of pagination; that is, if a row is expanded to show enough rows that there are more than the current page size, the other rows will be pushed to the next page. To prevent this behavior and to always show grouped rows on the same page as their parent, set tableConfig.paginateExpandedRows to false.

const dataTableProps = useDataTable({
// ...
tableConfig: {
paginateExpandedRows: false,
},
// ...
});

Component Tokens

Note: Click on the token row to copy the token to your clipboard.

DataTable Tokens

Token NameValue
data-table.color.border.column-header.drag
#002677
data-table.color.border.root
#CBCCCD
data-table.color.border.row.drag
#002677
data-table.color.border.table
#CBCCCD
data-table.color.icon.column-header-menus.grouping.active
#002677
data-table.color.icon.column-header-menus.grouping.hover
#004BA0
data-table.color.icon.column-header-menus.grouping.rest
#196ECF
data-table.color.icon.column-header-menus.sorting.active
#002677
data-table.color.icon.column-header-menus.sorting.hover
#004BA0
data-table.color.icon.column-header-menus.sorting.rest
#196ECF
data-table.color.icon.drag-handle.active
#002677
data-table.color.icon.drag-handle.hover
#004BA0
data-table.color.icon.drag-handle.rest
#196ECF
data-table.color.icon.expander.active
#002677
data-table.color.icon.expander.disabled
#7D7F81
data-table.color.icon.expander.hover
#004BA0
data-table.color.icon.expander.rest
#196ECF
data-table.color.icon.utility.drag-alternative.active
#000000
data-table.color.icon.utility.drag-alternative.disabled
#7D7F81
data-table.color.icon.utility.drag-alternative.hover
#323334
data-table.color.icon.utility.drag-alternative.rest
#4B4D4F
data-table.color.icon.utility.filter.active
#002677
data-table.color.icon.utility.filter.hover
#004BA0
data-table.color.icon.utility.filter.rest
#196ECF
data-table.color.surface.column-header.active
#E5F8FB
data-table.color.surface.column-header.default
#F3F3F3
data-table.color.surface.column-header.drag
#E5F8FB
data-table.color.surface.footer
#F3F3F3
data-table.color.surface.header
#FFFFFF
data-table.color.surface.root
#FFFFFF
data-table.color.surface.row.drag
#E5F8FB
data-table.color.surface.row.even
#FAFCFF
data-table.color.surface.row.highlighted
#E5F8FB
data-table.color.surface.row.hover
#F3F3F3
data-table.color.surface.row.odd
#FFFFFF
data-table.color.surface.table
#FFFFFF
data-table.color.text.cell
#4B4D4F
data-table.color.text.column-header
#4B4D4F
data-table.color.text.header.heading
#002677
data-table.color.text.header.paragraph
#4B4D4F
data-table.border-radius.all.container
8px
data-table.border-width.all.column-header.drag
2px
data-table.border-width.all.root
1px
data-table.border-width.all.row.drag
2px
data-table.border-width.all.table
1px
data-table.sizing.all.icon.column-header-menus
20px
data-table.sizing.all.icon.drag-handle-row
24px
data-table.sizing.all.icon.expander-column
24px
data-table.sizing.all.icon.utility.drag-alternative
20px
data-table.sizing.all.icon.utility.filter
20px
data-table.sizing.height.cell.comfortable
48px
data-table.sizing.height.cell.compact
32px
data-table.sizing.height.cell.cozy
40px
data-table.spacing.gap.horizontal.button-group
8px
data-table.spacing.gap.horizontal.cell
4px
data-table.spacing.gap.horizontal.drag-alternative
8px
data-table.spacing.gap.horizontal.input-container
8px
data-table.spacing.gap.horizontal.slot-wrapper
24px
data-table.spacing.gap.vertical.column-header
2px
data-table.spacing.gap.vertical.header
4px
data-table.spacing.gap.filter-two-inputs
16px
data-table.spacing.padding.all.column-header
8px
data-table.spacing.padding.all.column-header-menus
2px
data-table.spacing.padding.all.header
16px
data-table.spacing.padding.all.result-text
16px
data-table.spacing.padding.all.slot-wrapper
16px
data-table.spacing.padding.horizontal.cell
8px
data-table.spacing.padding.vertical.button-group
8px
data-table.spacing.padding.vertical.cell
4px
data-table.elevation.column.pinned.left
6px 0px 8px -2px rgba(0,0,0,0.16)
data-table.elevation.column.pinned.right
-6px 0px 8px -2px rgba(0,0,0,0.16)
data-table.elevation.column-header
0px 6px 8px -2px rgba(0,0,0,0.16)
data-table.elevation.table-settings-dropdown.section-header
0px 2px 4px -2px rgba(0,0,0,0.16)

Table of Contents