MRT logoMaterial React Table

Aggregation and Grouping Example

Grouping and Aggregation features are usually hard to implement, but MRT (thanks to TanStack Table) makes it easy.

You can group by a single 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 out grouping by various columns in the example below, and sorting by the aggregated columns, such as "age" or "salary".


Demo

Open Code SandboxOpen on GitHub
State
First Name
Last Name
Age
Gender
Salary
Alabama (7)Oldest by State:
64
Average by State:
$43,375
ThadWiegand64Female$56,146
AliviaLedner56Male$12,591
DanykaGleason36Male$71,238
LionelHartmann30Nonbinary$58,743
ReinholdReichel30Female$30,531
LurlineKoepp59Female$10,645
KodyBraun38Female$63,733
Alaska (8)Oldest by State:
59
Average by State:
$68,901
EloisaKohler31Male$45,801
KianHand56Male$81,062
LoyceSchmidt29Female$76,295
MichaleCollier59Male$75,197
EldridgeStroman42Male$59,594
AlveraBalistreri25Female$79,844
KaydenEmard35Female$98,252
DomingoBauch36Female$35,159
Arizona (1)Oldest by State:
22
Average by State:
$54,027
GunnerRolfson22Male$54,027
Arkansas (4)Oldest by State:
52
Average by State:
$58,194

Rows per page

1-20 of 249

Source Code

1import React, { FC, useMemo } from 'react';
2import { Box, Stack } from '@mui/material';
3import MaterialReactTable, { MRT_ColumnDef } from 'material-react-table';
4import { data, Person } from './makeData';
5
6const Example: FC = () => {
7 const averageSalary = useMemo(
8 () => data.reduce((acc, curr) => acc + curr.salary, 0) / data.length,
9 [],
10 );
11
12 const maxAge = useMemo(
13 () => data.reduce((acc, curr) => Math.max(acc, curr.age), 0),
14 [],
15 );
16
17 const columns = useMemo<MRT_ColumnDef<Person>[]>(
18 () => [
19 {
20 header: 'First Name',
21 accessorKey: 'firstName',
22 enableGrouping: false, //don't let this column be grouped
23 },
24 {
25 header: 'Last Name',
26 accessorKey: 'lastName',
27 },
28 {
29 header: 'Age',
30 accessorKey: 'age',
31 aggregationFn: 'max', //show the max age in the group (lots of pre-built aggregationFns to choose from)
32 //required to render an aggregated cell
33 AggregatedCell: ({ cell, table }) => (
34 <>
35 Oldest by{' '}
36 {table.getColumn(cell.row.groupingColumnId ?? '').columnDef.header}:{' '}
37 <Box
38 sx={{ color: 'info.main', display: 'inline', fontWeight: 'bold' }}
39 >
40 {cell.getValue<number>()}
41 </Box>
42 </>
43 ),
44 Footer: () => (
45 <Stack>
46 Max Age:
47 <Box color="warning.main">{Math.round(maxAge)}</Box>
48 </Stack>
49 ),
50 },
51 {
52 header: 'Gender',
53 accessorKey: 'gender',
54 //optionally, customize the cell render when this column is grouped. Make the text blue and pluralize the word
55 GroupedCell: ({ cell, row }) => (
56 <Box sx={{ color: 'primary.main' }}>
57 <strong>{cell.getValue<string>()}s </strong> ({row.subRows?.length})
58 </Box>
59 ),
60 },
61 {
62 header: 'State',
63 accessorKey: 'state',
64 },
65 {
66 header: 'Salary',
67 accessorKey: 'salary',
68 aggregationFn: 'mean',
69 //required to render an aggregated cell, show the average salary in the group
70 AggregatedCell: ({ cell, table }) => (
71 <>
72 Average by{' '}
73 {table.getColumn(cell.row.groupingColumnId ?? '').columnDef.header}:{' '}
74 <Box sx={{ color: 'success.main', fontWeight: 'bold' }}>
75 {cell.getValue<number>()?.toLocaleString?.('en-US', {
76 style: 'currency',
77 currency: 'USD',
78 minimumFractionDigits: 0,
79 maximumFractionDigits: 0,
80 })}
81 </Box>
82 </>
83 ),
84 //customize normal cell render on normal non-aggregated rows
85 Cell: ({ cell }) => (
86 <>
87 {cell.getValue<number>()?.toLocaleString?.('en-US', {
88 style: 'currency',
89 currency: 'USD',
90 minimumFractionDigits: 0,
91 maximumFractionDigits: 0,
92 })}
93 </>
94 ),
95 Footer: () => (
96 <Stack>
97 Average Salary:
98 <Box color="warning.main">
99 {averageSalary?.toLocaleString?.('en-US', {
100 style: 'currency',
101 currency: 'USD',
102 minimumFractionDigits: 0,
103 maximumFractionDigits: 0,
104 })}
105 </Box>
106 </Stack>
107 ),
108 },
109 ],
110 [averageSalary, maxAge],
111 );
112
113 return (
114 <MaterialReactTable
115 columns={columns}
116 data={data}
117 enableGrouping
118 enableStickyHeader
119 enableStickyFooter
120 initialState={{
121 density: 'compact',
122 expanded: true, //expand all groups by default
123 grouping: ['state'], //an array of columns to group by by default (can be multiple)
124 pagination: { pageIndex: 0, pageSize: 20 },
125 sorting: [{ id: 'state', desc: false }], //sort by state by default
126 }}
127 muiToolbarAlertBannerChipProps={{ color: 'primary' }}
128 muiTableContainerProps={{ sx: { maxHeight: 700 } }}
129 />
130 );
131};
132
133export default Example;
134

View Extra Storybook Examples