0
# Data Sorting
1
2
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.
3
4
## Capabilities
5
6
### Column Sorting (useSortBy)
7
8
Enables single and multi-column sorting with customizable sort algorithms and intuitive user interactions.
9
10
```javascript { .api }
11
/**
12
* Adds sorting capabilities to the table
13
* @param hooks - Hook registration object
14
*/
15
function useSortBy(hooks: Hooks): void;
16
17
interface SortByInstance {
18
/** Set the complete sort configuration */
19
setSortBy: (sortBy: SortingRule[]) => void;
20
/** Toggle sorting for a specific column */
21
toggleSortBy: (columnId: string, desc?: boolean, multi?: boolean) => void;
22
/** Clear all sorting */
23
resetSortBy: () => void;
24
/** Rows before sorting was applied */
25
preSortedRows: Row[];
26
/** Rows after sorting */
27
sortedRows: Row[];
28
}
29
30
interface SortByColumnInstance {
31
/** Whether this column can be sorted */
32
canSort: boolean;
33
/** Whether this column is currently sorted */
34
isSorted: boolean;
35
/** Sort direction (true = descending, false = ascending) */
36
isSortedDesc?: boolean;
37
/** Position in multi-sort order (0-based) */
38
sortedIndex?: number;
39
/** Toggle sorting for this column */
40
toggleSortBy: (desc?: boolean, multi?: boolean) => void;
41
/** Clear sorting for this column */
42
clearSortBy: () => void;
43
/** Get props for sort toggle UI element */
44
getSortByToggleProps: PropGetter;
45
}
46
47
interface SortingRule {
48
/** Column ID to sort by */
49
id: string;
50
/** Sort direction (true = descending, false = ascending) */
51
desc: boolean;
52
}
53
```
54
55
**Configuration Options:**
56
57
```javascript { .api }
58
interface SortByOptions {
59
/** Disable automatic sorting (handle sorting externally) */
60
manualSortBy?: boolean;
61
/** Disable all sorting */
62
disableSortBy?: boolean;
63
/** Disable multi-column sorting */
64
disableMultiSort?: boolean;
65
/** Function to detect multi-sort events (default: shift key) */
66
isMultiSortEvent?: (event: Event) => boolean;
67
/** Maximum number of columns that can be sorted simultaneously */
68
maxMultiSortColCount?: number;
69
/** Disable removal of sorting when clicking a sorted column */
70
disableSortRemove?: boolean;
71
/** Disable removal in multi-sort mode */
72
disableMultiRemove?: boolean;
73
/** Custom sorting function */
74
orderByFn?: (rows: Row[], sortBy: SortingRule[], sortTypes: SortTypes) => Row[];
75
/** Custom sort type definitions */
76
sortTypes?: Record<string, SortFunction>;
77
/** Auto-reset sorting when data changes */
78
autoResetSortBy?: boolean;
79
/** Initial sort state */
80
initialState?: {
81
sortBy?: SortingRule[];
82
};
83
}
84
85
interface SortFunction {
86
(rowA: Row, rowB: Row, columnId: string, desc: boolean): number;
87
}
88
```
89
90
**Usage Example:**
91
92
```javascript
93
import React from 'react';
94
import { useTable, useSortBy } from 'react-table';
95
96
function SortableTable({ columns, data }) {
97
const {
98
getTableProps,
99
getTableBodyProps,
100
headerGroups,
101
rows,
102
prepareRow,
103
state: { sortBy },
104
} = useTable(
105
{
106
columns,
107
data,
108
initialState: {
109
sortBy: [
110
{ id: 'name', desc: false }, // Sort by name ascending initially
111
],
112
},
113
},
114
useSortBy
115
);
116
117
return (
118
<div>
119
{/* Sort Status Display */}
120
<div>
121
{sortBy.length > 0 && (
122
<div>
123
Sorted by: {sortBy.map((sort, index) => (
124
<span key={sort.id}>
125
{index > 0 && ', '}
126
{sort.id} ({sort.desc ? 'desc' : 'asc'})
127
</span>
128
))}
129
</div>
130
)}
131
</div>
132
133
<table {...getTableProps()}>
134
<thead>
135
{headerGroups.map(headerGroup => (
136
<tr {...headerGroup.getHeaderGroupProps()}>
137
{headerGroup.headers.map(column => (
138
<th {...column.getHeaderProps(column.getSortByToggleProps())}>
139
{column.render('Header')}
140
{/* Sort indicator */}
141
<span>
142
{column.isSorted ? (
143
column.isSortedDesc ? (
144
' ↓'
145
) : (
146
' ↑'
147
)
148
) : (
149
column.canSort ? ' ↕' : ''
150
)}
151
</span>
152
{/* Multi-sort index */}
153
{column.isSorted && sortBy.length > 1 && (
154
<span>{column.sortedIndex + 1}</span>
155
)}
156
</th>
157
))}
158
</tr>
159
))}
160
</thead>
161
<tbody {...getTableBodyProps()}>
162
{rows.map(row => {
163
prepareRow(row);
164
return (
165
<tr {...row.getRowProps()}>
166
{row.cells.map(cell => (
167
<td {...cell.getCellProps()}>
168
{cell.render('Cell')}
169
</td>
170
))}
171
</tr>
172
);
173
})}
174
</tbody>
175
</table>
176
</div>
177
);
178
}
179
```
180
181
### Built-in Sort Types
182
183
React Table includes several built-in sort functions for different data types.
184
185
```javascript { .api }
186
interface SortTypes {
187
/** Mixed alphanumeric sorting (default) */
188
alphanumeric: SortFunction;
189
/** Date/time sorting */
190
datetime: SortFunction;
191
/** Basic comparison sorting */
192
basic: SortFunction;
193
/** String sorting with case handling */
194
string: SortFunction;
195
/** Numeric sorting with string cleaning */
196
number: SortFunction;
197
}
198
```
199
200
**Sort Type Examples:**
201
202
```javascript
203
const columns = [
204
{
205
Header: 'Product Name',
206
accessor: 'name',
207
sortType: 'string', // Case-insensitive string sort
208
},
209
{
210
Header: 'Price',
211
accessor: 'price',
212
sortType: 'number', // Numeric sort (strips non-numeric characters)
213
},
214
{
215
Header: 'Created Date',
216
accessor: 'createdAt',
217
sortType: 'datetime', // Date sorting
218
Cell: ({ value }) => new Date(value).toLocaleDateString(),
219
},
220
{
221
Header: 'Product Code',
222
accessor: 'code',
223
sortType: 'alphanumeric', // Mixed text/number sort
224
},
225
{
226
Header: 'Status',
227
accessor: 'status',
228
sortType: 'basic', // Simple comparison
229
},
230
];
231
```
232
233
### Multi-Column Sorting
234
235
Enable sorting by multiple columns simultaneously with intuitive user interactions.
236
237
```javascript
238
function MultiSortTable({ columns, data }) {
239
const {
240
getTableProps,
241
getTableBodyProps,
242
headerGroups,
243
rows,
244
prepareRow,
245
state: { sortBy },
246
} = useTable(
247
{
248
columns,
249
data,
250
// Multi-sort configuration
251
disableMultiSort: false,
252
maxMultiSortColCount: 3, // Limit to 3 columns
253
isMultiSortEvent: (e) => e.shiftKey, // Use Shift+click for multi-sort
254
},
255
useSortBy
256
);
257
258
return (
259
<div>
260
<div>
261
<small>
262
Hold Shift and click column headers to sort by multiple columns.
263
{sortBy.length > 1 && ` Currently sorting by ${sortBy.length} columns.`}
264
</small>
265
</div>
266
267
<table {...getTableProps()}>
268
<thead>
269
{headerGroups.map(headerGroup => (
270
<tr {...headerGroup.getHeaderGroupProps()}>
271
{headerGroup.headers.map(column => (
272
<th {...column.getHeaderProps(column.getSortByToggleProps())}>
273
<div>
274
{column.render('Header')}
275
{column.isSorted && (
276
<span>
277
{column.isSortedDesc ? ' ↓' : ' ↑'}
278
{sortBy.length > 1 && (
279
<sup>{column.sortedIndex + 1}</sup>
280
)}
281
</span>
282
)}
283
</div>
284
</th>
285
))}
286
</tr>
287
))}
288
</thead>
289
<tbody {...getTableBodyProps()}>
290
{rows.map(row => {
291
prepareRow(row);
292
return (
293
<tr {...row.getRowProps()}>
294
{row.cells.map(cell => (
295
<td {...cell.getCellProps()}>
296
{cell.render('Cell')}
297
</td>
298
))}
299
</tr>
300
);
301
})}
302
</tbody>
303
</table>
304
</div>
305
);
306
}
307
```
308
309
### Custom Sort Functions
310
311
Create custom sorting logic for specialized data types or business requirements.
312
313
```javascript { .api }
314
/**
315
* Custom sort function signature
316
* @param rowA - First row to compare
317
* @param rowB - Second row to compare
318
* @param columnId - ID of the column being sorted
319
* @param desc - Whether sorting in descending order
320
* @returns Comparison result (-1, 0, 1)
321
*/
322
interface CustomSortFunction {
323
(rowA: Row, rowB: Row, columnId: string, desc: boolean): number;
324
}
325
```
326
327
**Custom Sort Examples:**
328
329
```javascript
330
// Custom sort for semantic versions
331
const semverSort = (rowA, rowB, columnId) => {
332
const a = rowA.values[columnId];
333
const b = rowB.values[columnId];
334
335
const aParts = a.split('.').map(Number);
336
const bParts = b.split('.').map(Number);
337
338
for (let i = 0; i < Math.max(aParts.length, bParts.length); i++) {
339
const aPart = aParts[i] || 0;
340
const bPart = bParts[i] || 0;
341
342
if (aPart < bPart) return -1;
343
if (aPart > bPart) return 1;
344
}
345
346
return 0;
347
};
348
349
// Custom sort for priority levels
350
const prioritySort = (rowA, rowB, columnId) => {
351
const priorityOrder = { 'Low': 1, 'Medium': 2, 'High': 3, 'Critical': 4 };
352
const a = priorityOrder[rowA.values[columnId]] || 0;
353
const b = priorityOrder[rowB.values[columnId]] || 0;
354
355
return a < b ? -1 : a > b ? 1 : 0;
356
};
357
358
// Custom sort for file sizes
359
const fileSizeSort = (rowA, rowB, columnId) => {
360
const parseSize = (size) => {
361
const units = { 'B': 1, 'KB': 1024, 'MB': 1024**2, 'GB': 1024**3 };
362
const match = size.match(/^(\d+(?:\.\d+)?)\s*([A-Z]+)$/i);
363
if (!match) return 0;
364
return parseFloat(match[1]) * (units[match[2].toUpperCase()] || 1);
365
};
366
367
const a = parseSize(rowA.values[columnId]);
368
const b = parseSize(rowB.values[columnId]);
369
370
return a < b ? -1 : a > b ? 1 : 0;
371
};
372
373
// Register custom sort types
374
const table = useTable(
375
{
376
columns,
377
data,
378
sortTypes: {
379
semver: semverSort,
380
priority: prioritySort,
381
fileSize: fileSizeSort,
382
},
383
},
384
useSortBy
385
);
386
```
387
388
### Column Sort Configuration
389
390
Configure sorting behavior at the column level.
391
392
```javascript { .api }
393
interface ColumnSortConfig {
394
/** Sort type name or custom sort function */
395
sortType?: string | SortFunction;
396
/** Whether this column can be sorted */
397
disableSortBy?: boolean;
398
/** Whether to sort descending first (instead of ascending) */
399
sortDescFirst?: boolean;
400
/** Sort method for this specific column */
401
sortMethod?: SortFunction;
402
}
403
```
404
405
**Column Sort Examples:**
406
407
```javascript
408
const columns = [
409
{
410
Header: 'Name',
411
accessor: 'name',
412
sortType: 'string',
413
sortDescFirst: false, // Sort A-Z first, then Z-A
414
},
415
{
416
Header: 'Created Date',
417
accessor: 'createdAt',
418
sortType: 'datetime',
419
sortDescFirst: true, // Sort newest first by default
420
},
421
{
422
Header: 'File Size',
423
accessor: 'size',
424
sortType: 'fileSize', // Custom sort type
425
},
426
{
427
Header: 'Actions',
428
id: 'actions',
429
disableSortBy: true, // Not sortable
430
Cell: ({ row }) => <button>Edit</button>,
431
},
432
{
433
Header: 'Score',
434
accessor: 'score',
435
// Inline custom sort function
436
sortType: (rowA, rowB, columnId) => {
437
const a = rowA.values[columnId];
438
const b = rowB.values[columnId];
439
440
// Handle null/undefined scores
441
if (a == null) return b == null ? 0 : -1;
442
if (b == null) return 1;
443
444
return a < b ? -1 : a > b ? 1 : 0;
445
},
446
},
447
];
448
```
449
450
### Manual Sorting
451
452
Take control of sorting logic for server-side or custom implementations.
453
454
```javascript
455
function ManualSortTable({ columns, data }) {
456
const [sortBy, setSortBy] = React.useState([
457
{ id: 'name', desc: false }
458
]);
459
460
// Fetch sorted data based on current sort configuration
461
const sortedData = React.useMemo(() => {
462
return fetchSortedData(data, sortBy);
463
}, [data, sortBy]);
464
465
const {
466
getTableProps,
467
getTableBodyProps,
468
headerGroups,
469
rows,
470
prepareRow,
471
} = useTable(
472
{
473
columns,
474
data: sortedData,
475
manualSortBy: true, // Disable automatic sorting
476
// Provide current sort state
477
state: {
478
sortBy,
479
},
480
// Handle sort changes
481
onSortByChange: setSortBy,
482
},
483
useSortBy
484
);
485
486
return (
487
<div>
488
<div>
489
Server-side sorting: {sortBy.map(sort =>
490
`${sort.id} ${sort.desc ? 'desc' : 'asc'}`
491
).join(', ')}
492
</div>
493
494
<table {...getTableProps()}>
495
{/* Table implementation */}
496
</table>
497
</div>
498
);
499
}
500
```
501
502
### Advanced Sorting Features
503
504
Additional sorting utilities and methods for complex scenarios.
505
506
```javascript { .api }
507
interface SortByUtilities {
508
/** Default multi-column sorting function */
509
defaultOrderByFn: (
510
rows: Row[],
511
sortBy: SortingRule[],
512
sortTypes: SortTypes
513
) => Row[];
514
}
515
```
516
517
**Custom Sorting Algorithm Example:**
518
519
```javascript
520
// Custom multi-column sort with null handling
521
const customOrderByFn = (rows, sortBy, sortTypes) => {
522
return rows.slice().sort((rowA, rowB) => {
523
for (let i = 0; i < sortBy.length; i++) {
524
const { id, desc } = sortBy[i];
525
const sortType = sortTypes[id] || sortTypes.alphanumeric;
526
527
let result = sortType(rowA, rowB, id, desc);
528
529
// Handle null/undefined values
530
const aValue = rowA.values[id];
531
const bValue = rowB.values[id];
532
533
if (aValue == null && bValue == null) {
534
result = 0;
535
} else if (aValue == null) {
536
result = desc ? 1 : -1; // Nulls last
537
} else if (bValue == null) {
538
result = desc ? -1 : 1; // Nulls last
539
}
540
541
if (result !== 0) {
542
return desc ? -result : result;
543
}
544
}
545
return 0;
546
});
547
};
548
549
const table = useTable(
550
{
551
columns,
552
data,
553
orderByFn: customOrderByFn,
554
},
555
useSortBy
556
);
557
```
558
559
### Sort Toggle Props
560
561
The `getSortByToggleProps` method provides attributes for interactive sort controls.
562
563
```javascript
564
// Basic usage with default click handler
565
<th {...column.getHeaderProps(column.getSortByToggleProps())}>
566
{column.render('Header')}
567
</th>
568
569
// Custom click handler while keeping sort functionality
570
<th {...column.getHeaderProps(column.getSortByToggleProps({
571
onClick: (e) => {
572
console.log('Sorting column:', column.id);
573
// Default sort handling will still occur
574
},
575
className: 'sortable-header',
576
style: { userSelect: 'none' }
577
}))}>
578
{column.render('Header')}
579
</th>
580
581
// Completely custom sort button
582
<th {...column.getHeaderProps()}>
583
{column.render('Header')}
584
{column.canSort && (
585
<button
586
onClick={() => column.toggleSortBy()}
587
className={column.isSorted ? 'sorted' : ''}
588
>
589
Sort {column.isSorted && (column.isSortedDesc ? '↓' : '↑')}
590
</button>
591
)}
592
</th>
593
```
594
595
## Types
596
597
```javascript { .api }
598
interface TableState {
599
/** Current sort configuration */
600
sortBy: SortingRule[];
601
}
602
603
interface SortingRule {
604
/** Column ID */
605
id: string;
606
/** Sort direction */
607
desc: boolean;
608
}
609
610
interface SortTypes {
611
[key: string]: SortFunction;
612
}
613
614
interface SortMeta {
615
instance: TableInstance;
616
column: Column;
617
}
618
```