Hooks for building lightweight, fast and extendable datagrids for React
—
React Table provides comprehensive built-in filter types, sort types, and aggregation functions that handle common data operations. These pre-built functions can be used directly or serve as examples for creating custom implementations.
Pre-built filter functions for common filtering scenarios. These can be referenced by string name in column definitions.
interface FilterTypes {
/** Case-insensitive text search - checks if filter value is included in cell value */
text: (rows: Row[], columnId: string, filterValue: string) => Row[];
/** Case-insensitive exact text match */
exactText: (rows: Row[], columnId: string, filterValue: string) => Row[];
/** Case-sensitive exact text match */
exactTextCase: (rows: Row[], columnId: string, filterValue: string) => Row[];
/** Array includes filter value */
includes: (rows: Row[], columnId: string, filterValue: any) => Row[];
/** Array includes all filter values */
includesAll: (rows: Row[], columnId: string, filterValue: any[]) => Row[];
/** Array includes some filter values */
includesSome: (rows: Row[], columnId: string, filterValue: any[]) => Row[];
/** Cell value is included in filter value array */
includesValue: (rows: Row[], columnId: string, filterValue: any[]) => Row[];
/** Strict equality match (===) */
exact: (rows: Row[], columnId: string, filterValue: any) => Row[];
/** Loose equality match (==) */
equals: (rows: Row[], columnId: string, filterValue: any) => Row[];
/** Numeric range filter - value between min and max */
between: (rows: Row[], columnId: string, filterValue: [number, number]) => Row[];
}Usage Examples:
const columns = [
{
Header: 'Name',
accessor: 'name',
filter: 'text', // Case-insensitive text search
},
{
Header: 'Status',
accessor: 'status',
filter: 'exactText', // Exact match for status values
},
{
Header: 'Tags',
accessor: 'tags',
filter: 'includesAll', // All selected tags must be present
},
{
Header: 'Category',
accessor: 'category',
filter: 'includesValue', // Category must be in selected list
},
{
Header: 'Price',
accessor: 'price',
filter: 'between', // Price range filtering
},
];
// Custom filter value examples
const filterExample = () => {
const [filters, setFilters] = React.useState([
{ id: 'name', value: 'john' }, // Text search
{ id: 'status', value: 'Active' }, // Exact match
{ id: 'tags', value: ['urgent', 'featured'] }, // Include all tags
{ id: 'category', value: ['electronics', 'books'] }, // Category in list
{ id: 'price', value: [10, 100] }, // Price between 10 and 100
]);
return { filters, setFilters };
};Pre-built sorting functions for different data types and sorting scenarios.
interface SortTypes {
/**
* Default alphanumeric sort - handles mixed strings and numbers intelligently
* Sorts numbers numerically and strings alphabetically
*/
alphanumeric: (rowA: Row, rowB: Row, columnId: string, desc?: boolean) => number;
/**
* Date/time sorting - parses dates and sorts chronologically
* Handles various date formats and ISO strings
*/
datetime: (rowA: Row, rowB: Row, columnId: string, desc?: boolean) => number;
/**
* Basic comparison sort - uses simple comparison operators
* Good for primitive values
*/
basic: (rowA: Row, rowB: Row, columnId: string, desc?: boolean) => number;
/**
* String sort - case-insensitive string comparison
* Converts values to strings before comparing
*/
string: (rowA: Row, rowB: Row, columnId: string, desc?: boolean) => number;
/**
* Numeric sort - strips non-numeric characters and sorts numerically
* Handles currency symbols, percentages, etc.
*/
number: (rowA: Row, rowB: Row, columnId: string, desc?: boolean) => number;
}
/**
* Default sorting function - alias for alphanumeric sort
*/
function defaultOrderByFn(
rowA: Row,
rowB: Row,
columnId: string,
desc?: boolean
): number;Usage Examples:
const columns = [
{
Header: 'Product Name',
accessor: 'name',
sortType: 'alphanumeric', // Mixed text and numbers
},
{
Header: 'Created Date',
accessor: 'createdAt',
sortType: 'datetime', // Date sorting
},
{
Header: 'Price',
accessor: 'price',
sortType: 'number', // Numeric sorting (handles $1,234.56)
},
{
Header: 'Category',
accessor: 'category',
sortType: 'string', // Case-insensitive string sort
},
{
Header: 'Rating',
accessor: 'rating',
sortType: 'basic', // Simple numeric comparison
},
{
Header: 'SKU',
accessor: 'sku',
// Custom sort function
sortType: (rowA, rowB, columnId) => {
const a = rowA.values[columnId];
const b = rowB.values[columnId];
// Extract numeric part from SKU for sorting
const numA = parseInt(a.replace(/[^0-9]/g, ''), 10);
const numB = parseInt(b.replace(/[^0-9]/g, ''), 10);
return numA > numB ? 1 : -1;
},
},
];
// Sorting with initial state
const table = useTable(
{
columns,
data,
initialState: {
sortBy: [
{ id: 'createdAt', desc: true }, // Newest first
{ id: 'name', desc: false }, // Then alphabetical
],
},
},
useSortBy
);Pre-built aggregation functions for grouped data calculations.
interface AggregationTypes {
/**
* Sum all numeric values
* @param leafValues - Array of values to sum
* @returns Sum of all values
*/
sum: (leafValues: any[]) => number;
/**
* Find minimum value
* @param leafValues - Array of values
* @returns Minimum value
*/
min: (leafValues: any[]) => any;
/**
* Find maximum value
* @param leafValues - Array of values
* @returns Maximum value
*/
max: (leafValues: any[]) => any;
/**
* Create min-max range string
* @param leafValues - Array of values
* @returns String in format "min - max"
*/
minMax: (leafValues: any[]) => string;
/**
* Calculate average of values
* @param leafValues - Array of numeric values
* @returns Average value
*/
average: (leafValues: any[]) => number;
/**
* Calculate median value
* @param leafValues - Array of numeric values
* @returns Median value
*/
median: (leafValues: any[]) => number;
/**
* Get array of unique values
* @param leafValues - Array of values
* @returns Array of unique values
*/
unique: (leafValues: any[]) => any[];
/**
* Count unique values
* @param leafValues - Array of values
* @returns Number of unique values
*/
uniqueCount: (leafValues: any[]) => number;
/**
* Count all values
* @param leafValues - Array of values
* @returns Total count of values
*/
count: (leafValues: any[]) => number;
}Usage Examples:
import { useTable, useGroupBy } from 'react-table';
const columns = [
{
Header: 'Category',
accessor: 'category',
},
{
Header: 'Product',
accessor: 'product',
},
{
Header: 'Sales',
accessor: 'sales',
aggregate: 'sum', // Sum all sales in group
Aggregated: ({ value }) => (
<span style={{ fontWeight: 'bold' }}>
Total: ${value.toLocaleString()}
</span>
),
},
{
Header: 'Quantity',
accessor: 'quantity',
aggregate: 'sum',
Aggregated: ({ value }) => `${value} units`,
},
{
Header: 'Price Range',
accessor: 'price',
aggregate: 'minMax', // Show price range
Aggregated: ({ value }) => `$${value}`,
},
{
Header: 'Avg Rating',
accessor: 'rating',
aggregate: 'average',
Aggregated: ({ value }) => (
<span>{Math.round(value * 10) / 10} ⭐</span>
),
},
{
Header: 'Products',
accessor: 'product',
aggregate: 'uniqueCount', // Count unique products
Aggregated: ({ value }) => `${value} products`,
},
{
Header: 'Top Sellers',
accessor: 'product',
aggregate: 'unique', // List unique products
Aggregated: ({ value }) => (
<ul style={{ margin: 0, paddingLeft: '20px' }}>
{value.slice(0, 3).map((product, i) => (
<li key={i}>{product}</li>
))}
{value.length > 3 && <li>+{value.length - 3} more</li>}
</ul>
),
},
{
Header: 'Custom Metric',
accessor: 'sales',
// Custom aggregation function
aggregate: (leafValues, childRows) => {
const total = leafValues.reduce((sum, val) => sum + val, 0);
const count = leafValues.length;
const avg = total / count;
return {
total,
average: avg,
count,
efficiency: total / count > 1000 ? 'High' : 'Low',
};
},
Aggregated: ({ value }) => (
<div>
<div>Total: ${value.total}</div>
<div>Avg: ${Math.round(value.average)}</div>
<div>Efficiency: {value.efficiency}</div>
</div>
),
},
];
// Usage with grouping
function GroupedSalesTable({ data }) {
const table = useTable(
{
columns,
data,
initialState: {
groupBy: ['category'], // Group by category
},
},
useGroupBy
);
// Implementation...
}Examples of creating custom filter types, sort types, and aggregation functions.
// Custom filter type for date ranges
const dateRangeFilter = (rows, columnId, filterValue) => {
const [startDate, endDate] = filterValue;
return rows.filter(row => {
const cellValue = row.values[columnId];
const date = new Date(cellValue);
return date >= startDate && date <= endDate;
});
};
// Custom sort for version numbers (e.g., "1.2.3")
const versionSort = (rowA, rowB, columnId) => {
const versionA = rowA.values[columnId];
const versionB = rowB.values[columnId];
const parseVersion = (version) => {
return version.split('.').map(part => parseInt(part, 10));
};
const partsA = parseVersion(versionA);
const partsB = parseVersion(versionB);
for (let i = 0; i < Math.max(partsA.length, partsB.length); i++) {
const a = partsA[i] || 0;
const b = partsB[i] || 0;
if (a !== b) {
return a > b ? 1 : -1;
}
}
return 0;
};
// Custom aggregation for weighted average
const weightedAverageAggregator = (leafValues, childRows) => {
let totalValue = 0;
let totalWeight = 0;
childRows.forEach((row, index) => {
const value = leafValues[index];
const weight = row.original.weight || 1; // Default weight of 1
totalValue += value * weight;
totalWeight += weight;
});
return totalWeight > 0 ? totalValue / totalWeight : 0;
};
// Usage of custom types
const customColumns = [
{
Header: 'Release Date',
accessor: 'releaseDate',
filter: dateRangeFilter,
Filter: ({ column }) => (
<DateRangePicker
startDate={column.filterValue?.[0]}
endDate={column.filterValue?.[1]}
onChange={([start, end]) => column.setFilter([start, end])}
/>
),
},
{
Header: 'Version',
accessor: 'version',
sortType: versionSort,
},
{
Header: 'Score',
accessor: 'score',
aggregate: weightedAverageAggregator,
Aggregated: ({ value }) => `Weighted Avg: ${value.toFixed(2)}`,
},
];// Filter function signature
type FilterFunction = (
rows: Row[],
columnId: string,
filterValue: any,
addMeta?: (meta: any) => void
) => Row[];
// Sort function signature
type SortFunction = (
rowA: Row,
rowB: Row,
columnId: string,
desc?: boolean
) => number;
// Aggregation function signature
type AggregationFunction = (
leafValues: any[],
childRows: Row[]
) => any;
// Global filter function signature
type GlobalFilterFunction = (
rows: Row[],
columnIds: string[],
filterValue: any,
addMeta?: (meta: any) => void
) => Row[];
interface Column {
/** Filter type or custom filter function */
filter?: string | FilterFunction;
/** Sort type or custom sort function */
sortType?: string | SortFunction;
/** Aggregation type or custom aggregation function */
aggregate?: string | AggregationFunction;
/** Whether values should be sorted in descending order first */
sortDescFirst?: boolean;
/** Whether to invert the sort direction */
sortInverted?: boolean;
}Install with Tessl CLI
npx tessl i tessl/npm-react-table