Sorting
Add sortable: true to any column to enable client-side sorting on that column.
Click a header to sort; click again to reverse direction.
Columns without sortable are not interactive and are excluded from the tab order.
Sorting playground
Toggle the default sort column, direction, a custom icon, and per-column sort functions. The onSort readout shows the callback firing on every sort change.
import DataTable, { type TableColumn, SortOrder } from 'react-data-table-component';
const columns: TableColumn<Employee>[] = [
{ id: 'name', name: 'Name', selector: r => r.name, sortable: true },
{ id: 'dept', name: 'Department', selector: r => r.department, sortable: true },
{ id: 'salary', name: 'Salary', selector: r => r.salary, sortable: true,
format: r => `$${r.salary.toLocaleString()}`, right: true },
{ id: 'hired', name: 'Hired', selector: r => r.hired, sortable: true },
{ id: 'id', name: 'ID', selector: r => r.id }, // not sortable
];
<DataTable
columns={columns}
data={data}
defaultSortFieldId="name"
defaultSortAsc={true}
onSort={(column, direction) => console.log(column.id, direction)}
highlightOnHover
/> Default sort
defaultSortFieldId sets which column is sorted on first render.
Its value must match the id on the column definition.
defaultSortAsc controls the initial direction; it defaults to true (ascending).
// Sort by salary descending on load
<DataTable
columns={columns}
data={data}
defaultSortFieldId="salary"
defaultSortAsc={false}
/> Listening to sort events
onSort fires on every sort change, whether triggered by clicking a header or by
a defaultSortFieldId change. It receives the sorted column, the new direction, and
the fully sorted row array.
import { SortOrder } from 'react-data-table-component';
<DataTable
columns={columns}
data={data}
onSort={(column, direction, sortedRows) => {
console.log('sorted by', column.id, direction);
// sortedRows is the full sorted array after the sort is applied
}}
/> Custom sort function: per column
Override the sort comparator for a specific column with column.sortFunction.
It receives two row objects and returns a number, just like Array.prototype.sort.
Use this for locale-aware strings, currency values, or any non-default ordering.
const columns: TableColumn<Employee>[] = [
{
id: 'name',
name: 'Name',
selector: r => r.name,
sortable: true,
sortFunction: (a, b) => a.name.localeCompare(b.name, undefined, { sensitivity: 'base' }),
},
{
id: 'salary',
name: 'Salary',
selector: r => r.salary,
sortable: true,
sortFunction: (a, b) => a.salary - b.salary,
},
]; Custom sort function: table level
The table-level sortFunction prop replaces the entire sort algorithm for every
column. It receives the full row array, the selector function for the active column, and the
sort direction. Return the sorted array. This is less common than per-column sortFunction
but useful when you need a single unified sort strategy across all columns.
import { SortOrder, type SortFunction } from 'react-data-table-component';
const customSort: SortFunction<Employee> = (rows, selector, direction) => {
return [...rows].sort((a, b) => {
const aVal = selector(a);
const bVal = selector(b);
if (aVal === bVal) return 0;
const cmp = aVal > bVal ? 1 : -1;
return direction === SortOrder.ASC ? cmp : -cmp;
});
};
<DataTable columns={columns} data={data} sortFunction={customSort} /> Priority ordering: sort certain rows first
Sometimes you want specific rows to float to the top regardless of what column is sorted. There are two distinct patterns: sorting a column by a custom value order (e.g. status priority), and anchoring specific rows by identity regardless of sort. The latter is covered in depth on the Row pinning page.
Priority sort patterns
Two modes: sort a Status column by custom priority order (Active → On Leave → Terminated), and break ties within the same primary value using a secondary salary sort.
Sort Status by a custom priority: Active → On Leave → Terminated. Click the Status header.
import DataTable, { SortOrder, type TableColumn, type SortFunction } from 'react-data-table-component';
// ── Pattern 1: custom value ordering ───────────────────────────────────────
// Sort "Active → On Leave → Terminated" instead of alphabetically.
const STATUS_ORDER = { Active: 0, 'On Leave': 1, Terminated: 2 };
const columns: TableColumn<Employee>[] = [
{
id: 'status',
name: 'Status',
selector: r => STATUS_ORDER[r.status], // numeric selector drives the sort key
cell: r => <StatusBadge status={r.status} />,
sortable: true,
sortFunction: (a, b) => STATUS_ORDER[a.status] - STATUS_ORDER[b.status],
},
];
// ── Pattern 2: secondary sort (break ties) ──────────────────────────────────
// When two rows have the same primary value, sort by salary descending as a
// tiebreaker. Wire this as the table-level sortFunction.
const withSecondarySalary: SortFunction<Employee> = (rows, selector, direction) =>
[...rows].sort((a, b) => {
const av = selector(a), bv = selector(b);
const primary = av === bv ? 0 : (av > bv ? 1 : -1) * (direction === SortOrder.ASC ? 1 : -1);
return primary !== 0 ? primary : b.salary - a.salary; // salary desc as tiebreaker
}); Sorting null / empty values last
By default, empty strings and null sort ahead of real values alphabetically.
Push them to the bottom with a guard in your comparator:
const nullsLastSort: SortFunction<Employee> = (rows, selector, direction) =>
[...rows].sort((a, b) => {
const av = selector(a);
const bv = selector(b);
// Empty / null → always sink to bottom regardless of direction
const aEmpty = av === null || av === undefined || av === '';
const bEmpty = bv === null || bv === undefined || bv === '';
if (aEmpty && bEmpty) return 0;
if (aEmpty) return 1;
if (bEmpty) return -1;
const cmp = av > bv ? 1 : -1;
return direction === SortOrder.ASC ? cmp : -cmp;
});
<DataTable columns={columns} data={data} sortFunction={nullsLastSort} /> Custom sort icon
Pass any React node to sortIcon to replace the default chevron.
The icon is rendered inside a span that receives rdt_sortIconActive /
rdt_sortIconInactive and rdt_sortIconAsc class names. Use these to
style direction and active state with CSS.
// A simple triangle that flips via CSS
const SortIcon = () => (
<svg width="10" height="10" viewBox="0 0 10 10" fill="currentColor" aria-hidden="true">
<path d="M5 1l4 8H1z" />
</svg>
);
// Flip the icon for descending with CSS:
// .rdt_sortIcon:not(.rdt_sortIconAsc) svg { transform: rotate(180deg); }
<DataTable columns={columns} data={data} sortIcon={<SortIcon />} /> Server-side sorting
Set sortServer to stop the table from sorting rows locally.
DataTable still updates its visual sort indicator and fires onSort. Your handler
is responsible for fetching or reordering data and passing the updated array back via
data.
When a column maps to a different backend field name, set sortField on the column.
The value is passed to onSort via the column object so you can forward it directly
to your query.
Server-side sorting
Click any column header to trigger a fake server fetch (250 ms latency). The table shows a loading indicator while the sorted data arrives.
import { useState } from 'react';
import DataTable, { type TableColumn, SortOrder } from 'react-data-table-component';
const columns: TableColumn<Employee>[] = [
{ id: 'name', name: 'Name', selector: r => r.name, sortable: true, sortField: 'name' },
{ id: 'department', name: 'Department', selector: r => r.department, sortable: true, sortField: 'department' },
{ id: 'salary', name: 'Salary', selector: r => r.salary, sortable: true, sortField: 'salary',
right: true, format: r => `$${r.salary.toLocaleString()}` },
{ id: 'hired', name: 'Hired', selector: r => r.hired, sortable: true, sortField: 'hired' },
];
export default function App() {
const [data, setData] = useState<Employee[]>(initialData);
const [loading, setLoading] = useState(false);
async function handleSort(col: TableColumn<Employee>, dir: SortOrder) {
const field = col.sortField ?? String(col.id);
setLoading(true);
const sorted = await fetch(`/api/employees?sort=${field}&dir=${dir}`).then(r => r.json());
setData(sorted);
setLoading(false);
}
return (
<DataTable
columns={columns}
data={data}
progressPending={loading}
sortServer
onSort={handleSort}
highlightOnHover
/>
);
} Combined: server-side sorting and pagination
When using both sortServer and paginationServer, the three callbacks
are mutually exclusive. Only one fires per interaction.
Sorting resets the page to 1 automatically and onChangePage is suppressed, so
onSort is your only fetch point for a new sort. Always fetch from page 1 there
and update your local page state to 1 to stay in sync.
See the pagination docs for the complete callback contract.
Server-side sort + pagination
150-row simulated dataset. Sort any column, then page through the results. Sorting resets to page 1 automatically.
import { useState, useEffect, useCallback } from 'react';
import DataTable, { type TableColumn, SortOrder } from 'react-data-table-component';
export default function App() {
const [data, setData] = useState<Employee[]>([]);
const [loading, setLoading] = useState(true);
const [totalRows, setTotal] = useState(0);
const [page, setPage] = useState(1);
const [perPage, setPerPage] = useState(10);
const [sortField, setSortField] = useState('name');
const [sortDir, setSortDir] = useState<SortOrder>(SortOrder.ASC);
const [resetPage, setResetPage] = useState(false);
const load = useCallback(async (params) => {
setLoading(true);
const result = await fetchFromServer(params);
setData(result.rows);
setTotal(result.total);
setLoading(false);
}, []);
useEffect(() => { load({ page, perPage, sortField, sortDir }); }, []);
function handleSort(col: TableColumn<Employee>, dir: SortOrder) {
const field = col.sortField ?? String(col.id);
setSortField(field); setSortDir(dir);
setPage(1); setResetPage(prev => !prev); // reset pagination to page 1 on sort
load({ page: 1, perPage, sortField: field, sortDir: dir });
}
return (
<DataTable
columns={columns}
data={data}
progressPending={loading}
sortServer
onSort={handleSort}
pagination
paginationServer
paginationTotalRows={totalRows}
paginationResetDefaultPage={resetPage}
onChangePage={p => { setPage(p); load({ page: p, perPage, sortField, sortDir }); }}
onChangeRowsPerPage={(pp, p) => { setPerPage(pp); load({ page: p, perPage: pp, sortField, sortDir }); }}
highlightOnHover
/>
);
} Prop reference
| Prop | Type | Default | Description |
|---|---|---|---|
defaultSortFieldId | string | number | - | Column id to sort by on first render. |
defaultSortAsc | boolean | true | Initial sort direction. false = descending. |
onSort | (column, direction, sortedRows) => void | - | Called on every sort change with the active column, direction, and sorted rows. |
sortServer | boolean | false | Disable client-side sorting. Use with onSort to sort remotely. |
sortFunction | SortFunction<T> | - | Table-level sort algorithm. Replaces the default sort for all columns. |
sortIcon | ReactNode | - | Custom icon rendered inside sortable column headers. |
column.sortable | boolean | false | Enable sorting on this column. |
column.sortFunction | (a, b) => number | - | Per-column sort comparator. Takes priority over the table-level sortFunction. |
column.sortField | string | - | Backend field name passed to onSort when it differs from column.id. |