Hooks for building lightweight, fast and extendable datagrids for React
—
React Table provides comprehensive sorting capabilities including single-column sorting, multi-column sorting, and customizable sort types. The sorting system supports various data types and allows for complete customization of sorting behavior.
Enables single and multi-column sorting with customizable sort algorithms and intuitive user interactions.
/**
* Adds sorting capabilities to the table
* @param hooks - Hook registration object
*/
function useSortBy(hooks: Hooks): void;
interface SortByInstance {
/** Set the complete sort configuration */
setSortBy: (sortBy: SortingRule[]) => void;
/** Toggle sorting for a specific column */
toggleSortBy: (columnId: string, desc?: boolean, multi?: boolean) => void;
/** Clear all sorting */
resetSortBy: () => void;
/** Rows before sorting was applied */
preSortedRows: Row[];
/** Rows after sorting */
sortedRows: Row[];
}
interface SortByColumnInstance {
/** Whether this column can be sorted */
canSort: boolean;
/** Whether this column is currently sorted */
isSorted: boolean;
/** Sort direction (true = descending, false = ascending) */
isSortedDesc?: boolean;
/** Position in multi-sort order (0-based) */
sortedIndex?: number;
/** Toggle sorting for this column */
toggleSortBy: (desc?: boolean, multi?: boolean) => void;
/** Clear sorting for this column */
clearSortBy: () => void;
/** Get props for sort toggle UI element */
getSortByToggleProps: PropGetter;
}
interface SortingRule {
/** Column ID to sort by */
id: string;
/** Sort direction (true = descending, false = ascending) */
desc: boolean;
}Configuration Options:
interface SortByOptions {
/** Disable automatic sorting (handle sorting externally) */
manualSortBy?: boolean;
/** Disable all sorting */
disableSortBy?: boolean;
/** Disable multi-column sorting */
disableMultiSort?: boolean;
/** Function to detect multi-sort events (default: shift key) */
isMultiSortEvent?: (event: Event) => boolean;
/** Maximum number of columns that can be sorted simultaneously */
maxMultiSortColCount?: number;
/** Disable removal of sorting when clicking a sorted column */
disableSortRemove?: boolean;
/** Disable removal in multi-sort mode */
disableMultiRemove?: boolean;
/** Custom sorting function */
orderByFn?: (rows: Row[], sortBy: SortingRule[], sortTypes: SortTypes) => Row[];
/** Custom sort type definitions */
sortTypes?: Record<string, SortFunction>;
/** Auto-reset sorting when data changes */
autoResetSortBy?: boolean;
/** Initial sort state */
initialState?: {
sortBy?: SortingRule[];
};
}
interface SortFunction {
(rowA: Row, rowB: Row, columnId: string, desc: boolean): number;
}Usage Example:
import React from 'react';
import { useTable, useSortBy } from 'react-table';
function SortableTable({ columns, data }) {
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
prepareRow,
state: { sortBy },
} = useTable(
{
columns,
data,
initialState: {
sortBy: [
{ id: 'name', desc: false }, // Sort by name ascending initially
],
},
},
useSortBy
);
return (
<div>
{/* Sort Status Display */}
<div>
{sortBy.length > 0 && (
<div>
Sorted by: {sortBy.map((sort, index) => (
<span key={sort.id}>
{index > 0 && ', '}
{sort.id} ({sort.desc ? 'desc' : 'asc'})
</span>
))}
</div>
)}
</div>
<table {...getTableProps()}>
<thead>
{headerGroups.map(headerGroup => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map(column => (
<th {...column.getHeaderProps(column.getSortByToggleProps())}>
{column.render('Header')}
{/* Sort indicator */}
<span>
{column.isSorted ? (
column.isSortedDesc ? (
' ↓'
) : (
' ↑'
)
) : (
column.canSort ? ' ↕' : ''
)}
</span>
{/* Multi-sort index */}
{column.isSorted && sortBy.length > 1 && (
<span>{column.sortedIndex + 1}</span>
)}
</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{rows.map(row => {
prepareRow(row);
return (
<tr {...row.getRowProps()}>
{row.cells.map(cell => (
<td {...cell.getCellProps()}>
{cell.render('Cell')}
</td>
))}
</tr>
);
})}
</tbody>
</table>
</div>
);
}React Table includes several built-in sort functions for different data types.
interface SortTypes {
/** Mixed alphanumeric sorting (default) */
alphanumeric: SortFunction;
/** Date/time sorting */
datetime: SortFunction;
/** Basic comparison sorting */
basic: SortFunction;
/** String sorting with case handling */
string: SortFunction;
/** Numeric sorting with string cleaning */
number: SortFunction;
}Sort Type Examples:
const columns = [
{
Header: 'Product Name',
accessor: 'name',
sortType: 'string', // Case-insensitive string sort
},
{
Header: 'Price',
accessor: 'price',
sortType: 'number', // Numeric sort (strips non-numeric characters)
},
{
Header: 'Created Date',
accessor: 'createdAt',
sortType: 'datetime', // Date sorting
Cell: ({ value }) => new Date(value).toLocaleDateString(),
},
{
Header: 'Product Code',
accessor: 'code',
sortType: 'alphanumeric', // Mixed text/number sort
},
{
Header: 'Status',
accessor: 'status',
sortType: 'basic', // Simple comparison
},
];Enable sorting by multiple columns simultaneously with intuitive user interactions.
function MultiSortTable({ columns, data }) {
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
prepareRow,
state: { sortBy },
} = useTable(
{
columns,
data,
// Multi-sort configuration
disableMultiSort: false,
maxMultiSortColCount: 3, // Limit to 3 columns
isMultiSortEvent: (e) => e.shiftKey, // Use Shift+click for multi-sort
},
useSortBy
);
return (
<div>
<div>
<small>
Hold Shift and click column headers to sort by multiple columns.
{sortBy.length > 1 && ` Currently sorting by ${sortBy.length} columns.`}
</small>
</div>
<table {...getTableProps()}>
<thead>
{headerGroups.map(headerGroup => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map(column => (
<th {...column.getHeaderProps(column.getSortByToggleProps())}>
<div>
{column.render('Header')}
{column.isSorted && (
<span>
{column.isSortedDesc ? ' ↓' : ' ↑'}
{sortBy.length > 1 && (
<sup>{column.sortedIndex + 1}</sup>
)}
</span>
)}
</div>
</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{rows.map(row => {
prepareRow(row);
return (
<tr {...row.getRowProps()}>
{row.cells.map(cell => (
<td {...cell.getCellProps()}>
{cell.render('Cell')}
</td>
))}
</tr>
);
})}
</tbody>
</table>
</div>
);
}Create custom sorting logic for specialized data types or business requirements.
/**
* Custom sort function signature
* @param rowA - First row to compare
* @param rowB - Second row to compare
* @param columnId - ID of the column being sorted
* @param desc - Whether sorting in descending order
* @returns Comparison result (-1, 0, 1)
*/
interface CustomSortFunction {
(rowA: Row, rowB: Row, columnId: string, desc: boolean): number;
}Custom Sort Examples:
// Custom sort for semantic versions
const semverSort = (rowA, rowB, columnId) => {
const a = rowA.values[columnId];
const b = rowB.values[columnId];
const aParts = a.split('.').map(Number);
const bParts = b.split('.').map(Number);
for (let i = 0; i < Math.max(aParts.length, bParts.length); i++) {
const aPart = aParts[i] || 0;
const bPart = bParts[i] || 0;
if (aPart < bPart) return -1;
if (aPart > bPart) return 1;
}
return 0;
};
// Custom sort for priority levels
const prioritySort = (rowA, rowB, columnId) => {
const priorityOrder = { 'Low': 1, 'Medium': 2, 'High': 3, 'Critical': 4 };
const a = priorityOrder[rowA.values[columnId]] || 0;
const b = priorityOrder[rowB.values[columnId]] || 0;
return a < b ? -1 : a > b ? 1 : 0;
};
// Custom sort for file sizes
const fileSizeSort = (rowA, rowB, columnId) => {
const parseSize = (size) => {
const units = { 'B': 1, 'KB': 1024, 'MB': 1024**2, 'GB': 1024**3 };
const match = size.match(/^(\d+(?:\.\d+)?)\s*([A-Z]+)$/i);
if (!match) return 0;
return parseFloat(match[1]) * (units[match[2].toUpperCase()] || 1);
};
const a = parseSize(rowA.values[columnId]);
const b = parseSize(rowB.values[columnId]);
return a < b ? -1 : a > b ? 1 : 0;
};
// Register custom sort types
const table = useTable(
{
columns,
data,
sortTypes: {
semver: semverSort,
priority: prioritySort,
fileSize: fileSizeSort,
},
},
useSortBy
);Configure sorting behavior at the column level.
interface ColumnSortConfig {
/** Sort type name or custom sort function */
sortType?: string | SortFunction;
/** Whether this column can be sorted */
disableSortBy?: boolean;
/** Whether to sort descending first (instead of ascending) */
sortDescFirst?: boolean;
/** Sort method for this specific column */
sortMethod?: SortFunction;
}Column Sort Examples:
const columns = [
{
Header: 'Name',
accessor: 'name',
sortType: 'string',
sortDescFirst: false, // Sort A-Z first, then Z-A
},
{
Header: 'Created Date',
accessor: 'createdAt',
sortType: 'datetime',
sortDescFirst: true, // Sort newest first by default
},
{
Header: 'File Size',
accessor: 'size',
sortType: 'fileSize', // Custom sort type
},
{
Header: 'Actions',
id: 'actions',
disableSortBy: true, // Not sortable
Cell: ({ row }) => <button>Edit</button>,
},
{
Header: 'Score',
accessor: 'score',
// Inline custom sort function
sortType: (rowA, rowB, columnId) => {
const a = rowA.values[columnId];
const b = rowB.values[columnId];
// Handle null/undefined scores
if (a == null) return b == null ? 0 : -1;
if (b == null) return 1;
return a < b ? -1 : a > b ? 1 : 0;
},
},
];Take control of sorting logic for server-side or custom implementations.
function ManualSortTable({ columns, data }) {
const [sortBy, setSortBy] = React.useState([
{ id: 'name', desc: false }
]);
// Fetch sorted data based on current sort configuration
const sortedData = React.useMemo(() => {
return fetchSortedData(data, sortBy);
}, [data, sortBy]);
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
prepareRow,
} = useTable(
{
columns,
data: sortedData,
manualSortBy: true, // Disable automatic sorting
// Provide current sort state
state: {
sortBy,
},
// Handle sort changes
onSortByChange: setSortBy,
},
useSortBy
);
return (
<div>
<div>
Server-side sorting: {sortBy.map(sort =>
`${sort.id} ${sort.desc ? 'desc' : 'asc'}`
).join(', ')}
</div>
<table {...getTableProps()}>
{/* Table implementation */}
</table>
</div>
);
}Additional sorting utilities and methods for complex scenarios.
interface SortByUtilities {
/** Default multi-column sorting function */
defaultOrderByFn: (
rows: Row[],
sortBy: SortingRule[],
sortTypes: SortTypes
) => Row[];
}Custom Sorting Algorithm Example:
// Custom multi-column sort with null handling
const customOrderByFn = (rows, sortBy, sortTypes) => {
return rows.slice().sort((rowA, rowB) => {
for (let i = 0; i < sortBy.length; i++) {
const { id, desc } = sortBy[i];
const sortType = sortTypes[id] || sortTypes.alphanumeric;
let result = sortType(rowA, rowB, id, desc);
// Handle null/undefined values
const aValue = rowA.values[id];
const bValue = rowB.values[id];
if (aValue == null && bValue == null) {
result = 0;
} else if (aValue == null) {
result = desc ? 1 : -1; // Nulls last
} else if (bValue == null) {
result = desc ? -1 : 1; // Nulls last
}
if (result !== 0) {
return desc ? -result : result;
}
}
return 0;
});
};
const table = useTable(
{
columns,
data,
orderByFn: customOrderByFn,
},
useSortBy
);The getSortByToggleProps method provides attributes for interactive sort controls.
// Basic usage with default click handler
<th {...column.getHeaderProps(column.getSortByToggleProps())}>
{column.render('Header')}
</th>
// Custom click handler while keeping sort functionality
<th {...column.getHeaderProps(column.getSortByToggleProps({
onClick: (e) => {
console.log('Sorting column:', column.id);
// Default sort handling will still occur
},
className: 'sortable-header',
style: { userSelect: 'none' }
}))}>
{column.render('Header')}
</th>
// Completely custom sort button
<th {...column.getHeaderProps()}>
{column.render('Header')}
{column.canSort && (
<button
onClick={() => column.toggleSortBy()}
className={column.isSorted ? 'sorted' : ''}
>
Sort {column.isSorted && (column.isSortedDesc ? '↓' : '↑')}
</button>
)}
</th>interface TableState {
/** Current sort configuration */
sortBy: SortingRule[];
}
interface SortingRule {
/** Column ID */
id: string;
/** Sort direction */
desc: boolean;
}
interface SortTypes {
[key: string]: SortFunction;
}
interface SortMeta {
instance: TableInstance;
column: Column;
}Install with Tessl CLI
npx tessl i tessl/npm-react-table