Guides
Aggregation Guide
How to use and customize the aggregation and grouping features of Shadcn Table
Shadcn Table has built-in aggregation features. There are options for both automatic client-side grouping and aggregation, as well as manual server-side grouping and aggregation. This guide will walk you through the different options and how to use and customize them.
See the Column Grouping Guide as a prerequisite to this guide. The Aggregation and Grouping Guide was recently split into two separate guides.
Relevant Table Options
Relevant Column Options
Relevant State
Aggregation on Grouped Rows
One of the cool features of Shadcn Table is that it can automatically aggregate the data in grouped rows. To enable this, you must specify both an aggregationFn and an AggregatedCell render option on a column definition.
Built-in Aggregation Functions
There are several built-in aggregation functions available that you can use. They are:
count- Finds the number of rows in a groupextent- Finds the minimum and maximum values of a group of rowsmax- Finds the maximum value of a group of rowsmean- Finds the average value of a group of rowsmedian- Finds the median value of a group of rowsmin- Finds the minimum value of a group of rowssum- sums the values of a group of rowsuniqueCount- Finds the number of unique values of a group of rowsunique- Finds the unique values of a group of rows
All of these built-in aggregation functions are from TanStack Table
const columns = [
{
accessorKey: 'team', //grouped by team in initial state below
header: 'Team',
},
{
accessorKey: 'player',
header: 'Player',
},
{
accessorKey: 'points',
header: 'Points',
aggregationFn: 'sum', //calc total points for each team by adding up all the points for each player on the team
AggregatedCell: ({ cell }) => <div>Team Score: {cell.getValue()}</div>,
},
];
const table = useShadcnTable({
columns,
data,
enableGrouping: true,
initialState: { grouping: ['team'], expanded: true },
});
return <ShadcnTable table={table} />;Custom Aggregation Functions
If none of these pre-built aggregation functions work for you, you can also pass in a custom aggregation definition. Since TanStack Table v9 a custom aggregation is a constructAggregationFn({ aggregate, merge? }) definition, not a plain callable: aggregate receives a context with the rows to aggregate and a getValue(row) accessor and returns the single value that will be displayed in the aggregated cell.
import { constructAggregationFn } from '@tanstack/react-table';
const columns = [
{
accessorKey: 'points',
header: 'Points',
aggregationFn: constructAggregationFn({
aggregate: ({ rows, getValue }) =>
rows.reduce((sum, row) => sum + Number(getValue(row) ?? 0), 0),
}),
AggregatedCell: ({ cell }) => <div>Team Score: {cell.getValue()}</div>,
},
];Register reusable definitions by name in the aggregationFns table option and reference them from aggregationFn: 'myAggregation'.
Aggregation on All Rows in Footer
Shadcn Table does not automatically aggregate all rows for you to calculate totals for the entire table. However, it is still easy enough to do this manually and add in your custom calculations into the footer or Footer of a column definition. It is recommended that you do any necessary aggregation calculations on your data in a useMemo hook before passing it to the columns footer in your columns definition.
//calculate the total points for all players in the table in a useMemo hook
const averageScore = useMemo(() => {
const totalPoints = data.reduce((acc, row) => acc + row.points, 0);
const totalPlayers = data.length;
return totalPoints / totalPlayers;
}, [data]);
const columns = useMemo(
() => [
{
accessorKey: 'name',
header: 'Name',
},
{
accessorKey: 'score',
header: 'Score',
Footer: () => <div>Average Score: {averageScore}</div>, //do not do calculations in render, do them in useMemo hook and pass them in here
},
],
[averageScore],
);Please remember to perform heavy aggregation calculations in a useMemo hook to avoid unnecessary re-renders!
Custom Cell Renders for Aggregation and Grouping
There are a few custom cell render overrides that you should be aware of when using grouping and aggregation features.
AggregatedCell Column Option
"Aggregation Cells" are cells in an aggregated row (not a normal data row) that can display aggregates (avg, sum, etc.) of the data in a group. The cell that the table is grouped on, however, is not an Aggregate Cell, but rather a GroupedCell.
You can specify the custom render for these cells with the AggregatedCell render option on a column definition.
const columns = [
{
accessorKey: 'points',
header: 'Points',
aggregationFn: 'sum',
AggregatedCell: ({ cell }) => <div>Total Score: {cell.getValue()}</div>,
},
];GroupedCell Column Option
"Grouped Cells" are cells in a grouped row (not a normal data row) that by default display the value that the rows are grouped on and the number of rows in the group. You can override the default render for these cells with the GroupedCell render option on a column definition.
const columns = [
{
accessorKey: 'team',
header: 'Team',
GroupedCell: ({ cell }) => <div>Team: {cell.getValue()}</div>,
},
];PlaceholderCell Column Option
"Placeholder Cells" are cells that are usually meant to be empty in grouped rows and columns. They are simply rendered with a value of null by default, but you can override the default render for these cells with the PlaceholderCell render option on a column definition.
const columns = [
{
accessorKey: 'team',
header: 'Team',
PlaceholderCell: ({ cell, row }) => (
<div>{row.original.someOtherRowValue}</div>
),
},
];Aggregation/Grouping Example
Multiple Aggregations Per column
You may want to calculate more than one aggregation per column. You can specify an array of aggregationFns, and then reference the aggregation results from an array in the AggregatedCell render option.
const columns = [
{
header: 'Salary',
accessorKey: 'salary',
aggregationFn: ['count', 'mean'], //multiple aggregation functions
AggregatedCell: ({ cell, table }) => (
<div>
{/*get the count from the first aggregation*/}
<div>Count: {cell.getValue()[0]}</div>
{/*get the average from the second aggregation*/}
<div>Average Salary: {cell.getValue()[1]}</div>
</div>
),
},
];Manual Grouping
Manual Grouping means that the data that you pass to the table is already grouped and aggregated, and you do not want Shadcn Table to do any of the grouping or aggregation for you. This is useful if you are using a backend API to do the grouping and aggregation for you, and you just want to display the results. However, you will need to put your data in the specific format that the expanding features understand.