Aggregation Example
Grouping and Aggregation features are usually hard to implement, but MRT (thanks to TanStack Table) makes it easy. Once enabled, simply click the vertical ellipses (⋮) icon for the column you want to group by and select Group by (column name).
You can group by a single column or multiple columns at a time. Then, you can run aggregations on grouped columns to calculate totals, averages, max, min, etc.
The Grouping and Aggregation features work hand in hand with the Expanding and Sorting features. Try grouping by various columns in the example below and sorting by the aggregated columns, such as "age" or "salary".
See the Column Grouping and Aggregation docs for more information.
State | First Name | Last Name | Age | Gender | Salary | ||
---|---|---|---|---|---|---|---|
Alabama (7) | Oldest by State: 64 | Average by State: $43,375 | |||||
Thad | Wiegand | 64 | Female | $56,146 | |||
Alivia | Ledner | 56 | Male | $12,591 | |||
Danyka | Gleason | 36 | Male | $71,238 | |||
Lionel | Hartmann | 30 | Nonbinary | $58,743 | |||
Reinhold | Reichel | 30 | Female | $30,531 | |||
Lurline | Koepp | 59 | Female | $10,645 | |||
Kody | Braun | 38 | Female | $63,733 | |||
Alaska (8) | Oldest by State: 59 | Average by State: $68,901 | |||||
Eloisa | Kohler | 31 | Male | $45,801 | |||
Kian | Hand | 56 | Male | $81,062 | |||
Loyce | Schmidt | 29 | Female | $76,295 | |||
Michale | Collier | 59 | Male | $75,197 | |||
Eldridge | Stroman | 42 | Male | $59,594 | |||
Alvera | Balistreri | 25 | Female | $79,844 | |||
Kayden | Emard | 35 | Female | $98,252 | |||
Domingo | Bauch | 36 | Female | $35,159 | |||
Arizona (1) | Oldest by State: 22 | Average by State: $54,027 | |||||
Gunner | Rolfson | 22 | Male | $54,027 | |||
Arkansas (4) | Oldest by State: 52 | Average by State: $58,194 | |||||
Max Age: 65 | Average Salary: $56,319 |
20
1import { useMemo } from 'react';2import { Box, Stack } from '@mui/material';3import {4 MaterialReactTable,5 useMaterialReactTable,6 type MRT_ColumnDef,7} from 'material-react-table';8import { data, type Person } from './makeData';910const Example = () => {11 const averageSalary = useMemo(12 () => data.reduce((acc, curr) => acc + curr.salary, 0) / data.length,13 [],14 );1516 const maxAge = useMemo(17 () => data.reduce((acc, curr) => Math.max(acc, curr.age), 0),18 [],19 );2021 const columns = useMemo<MRT_ColumnDef<Person>[]>(22 () => [23 {24 header: 'First Name',25 accessorKey: 'firstName',26 enableGrouping: false, //do not let this column be grouped27 },28 {29 header: 'Last Name',30 accessorKey: 'lastName',31 },32 {33 header: 'Age',34 accessorKey: 'age',35 aggregationFn: 'max', //show the max age in the group (lots of pre-built aggregationFns to choose from)36 //required to render an aggregated cell37 AggregatedCell: ({ cell, table }) => (38 <>39 Oldest by{' '}40 {table.getColumn(cell.row.groupingColumnId ?? '').columnDef.header}:{' '}41 <Box42 sx={{ color: 'info.main', display: 'inline', fontWeight: 'bold' }}43 >44 {cell.getValue<number>()}45 </Box>46 </>47 ),48 Footer: () => (49 <Stack>50 Max Age:51 <Box color="warning.main">{Math.round(maxAge)}</Box>52 </Stack>53 ),54 },55 {56 header: 'Gender',57 accessorKey: 'gender',58 //optionally, customize the cell render when this column is grouped. Make the text blue and pluralize the word59 GroupedCell: ({ cell, row }) => (60 <Box sx={{ color: 'primary.main' }}>61 <strong>{cell.getValue<string>()}s </strong> ({row.subRows?.length})62 </Box>63 ),64 },65 {66 header: 'State',67 accessorKey: 'state',68 },69 {70 header: 'Salary',71 accessorKey: 'salary',72 aggregationFn: 'mean',73 //required to render an aggregated cell, show the average salary in the group74 AggregatedCell: ({ cell, table }) => (75 <>76 Average by{' '}77 {table.getColumn(cell.row.groupingColumnId ?? '').columnDef.header}:{' '}78 <Box sx={{ color: 'success.main', fontWeight: 'bold' }}>79 {cell.getValue<number>()?.toLocaleString?.('en-US', {80 style: 'currency',81 currency: 'USD',82 minimumFractionDigits: 0,83 maximumFractionDigits: 0,84 })}85 </Box>86 </>87 ),88 //customize normal cell render on normal non-aggregated rows89 Cell: ({ cell }) => (90 <>91 {cell.getValue<number>()?.toLocaleString?.('en-US', {92 style: 'currency',93 currency: 'USD',94 minimumFractionDigits: 0,95 maximumFractionDigits: 0,96 })}97 </>98 ),99 Footer: () => (100 <Stack>101 Average Salary:102 <Box color="warning.main">103 {averageSalary?.toLocaleString?.('en-US', {104 style: 'currency',105 currency: 'USD',106 minimumFractionDigits: 0,107 maximumFractionDigits: 0,108 })}109 </Box>110 </Stack>111 ),112 },113 ],114 [averageSalary, maxAge],115 );116117 const table = useMaterialReactTable({118 columns,119 data,120 displayColumnDefOptions: {121 'mrt-row-expand': {122 enableResizing: true,123 },124 },125 enableColumnResizing: true,126 enableGrouping: true,127 enableStickyHeader: true,128 enableStickyFooter: true,129 initialState: {130 density: 'compact',131 expanded: true, //expand all groups by default132 grouping: ['state'], //an array of columns to group by by default (can be multiple)133 pagination: { pageIndex: 0, pageSize: 20 },134 sorting: [{ id: 'state', desc: false }], //sort by state by default135 },136 muiToolbarAlertBannerChipProps: { color: 'primary' },137 muiTableContainerProps: { sx: { maxHeight: 700 } },138 });139140 return <MaterialReactTable table={table} />;141};142143export default Example;144
View Extra Storybook Examples