Skip to content

Sorting & Filtering

Built-in sorting and filtering with WASM acceleration for large datasets.

Columns are sortable by default. Click headers to sort:

<DataGrid
data={users}
columns={columns}
rowKey="id"
/>
  • Click once: Sort ascending (▲)
  • Click again: Sort descending (▼)
  • Click again: Clear sort

Disable sorting for specific columns:

const columns = [
{ field: 'name', header: 'Name', sortable: true }, // Sortable
{ field: 'actions', header: 'Actions', sortable: false }, // Not sortable
];

The grid sorts values by their natural type:

  • Numbers: Numeric comparison
  • Strings: Locale-aware string comparison
  • Dates: Chronological order

For custom sort behavior, transform your data before passing to the grid.

<DataGrid
data={users}
columns={columns}
rowKey="id"
showFilter
filterPlaceholder="Search users..."
/>

By default, all columns are searchable. Limit to specific fields:

<DataGrid
data={users}
columns={columns}
rowKey="id"
showFilter
filterFields={['name', 'email']} // Only search these
/>

The filter uses substring matching:

With the optional WASM core, filtering uses trigram indexing for O(1) lookups:

Terminal window
npm install @askturret/grid-wasm

Performance comparison (100k rows):

MethodFilter Time
JavaScript linear scan~80ms
WASM trigram index<1ms

The grid automatically uses WASM when available.

Control the filter from outside the grid:

function FilteredGrid() {
const [filter, setFilter] = useState('');
const [data, setData] = useState(allData);
// Apply filter externally
useEffect(() => {
if (!filter) {
setData(allData);
} else {
setData(allData.filter(row =>
row.name.toLowerCase().includes(filter.toLowerCase())
));
}
}, [filter, allData]);
return (
<>
<input
value={filter}
onChange={(e) => setFilter(e.target.value)}
placeholder="Search..."
/>
<DataGrid
data={data} // Pre-filtered data
columns={columns}
rowKey="id"
/>
</>
);
}

Combine grid filter with external filters:

function AdvancedFiltering() {
const [statusFilter, setStatusFilter] = useState('all');
// Filter by status first
const statusFiltered = useMemo(() => {
if (statusFilter === 'all') return allData;
return allData.filter(row => row.status === statusFilter);
}, [statusFilter, allData]);
return (
<>
<select value={statusFilter} onChange={(e) => setStatusFilter(e.target.value)}>
<option value="all">All Statuses</option>
<option value="active">Active</option>
<option value="pending">Pending</option>
</select>
<DataGrid
data={statusFiltered} // Pre-filtered by status
columns={columns}
rowKey="id"
showFilter // Grid filter for text search
/>
</>
);
}

For datasets over 10k rows:

  1. Use WASM core for trigram indexing
  2. Debounce filter input to avoid excessive re-renders
  3. Virtualization is automatic at 100+ rows
Row CountSort Time (WASM)
10,0002ms
100,0005ms
1,000,00018ms
Row CountFilter Time
10,000<1ms
100,000<1ms
1,000,000<2ms

Customize the empty message:

<DataGrid
data={filteredData}
columns={columns}
rowKey="id"
showFilter
emptyMessage="No matching results"
/>

The empty message shows when:

  • Data array is empty
  • All rows are filtered out