Skip to main content

Filtering, Sorting, and Selection

The DataTable provides robust capabilities for interacting with your data, including column-based filtering, sorting, and row selection.

Sorting

To enable sorting on a specific column, set the sortable property to true in its configuration. By default, the table attempts to automatically guess the best sorting strategy. You can explicitly set a built-in strategy using sortingFn, or provide a custom sorting function.

note

Multi-column sorting is currently disabled. Only single-column sorting is supported.

Built-in Sorting Modes

The sortingFn property accepts any of these built-in mode names:

ModeDescription
autoAutomatically detect the best strategy
alphanumericCase-insensitive alphanumeric sort
alphanumericCaseSensitiveCase-sensitive alphanumeric sort
textCase-insensitive text sort
textCaseSensitiveCase-sensitive text sort
datetimeSort by date/time values
basicBasic comparison (greater than / less than)
const columns = [
{
id: 'name',
name: 'Name',
accessorKey: 'name',
sortable: true,
sortingFn: 'text', // explicit built-in sorting
},
{
id: 'score',
name: 'Score',
accessorKey: 'score',
sortable: true,
// Custom sorting function
sortingFn: (rowA, rowB, columnId) => {
const a = rowA.getValue(columnId);
const b = rowB.getValue(columnId);
// Custom logic: push undefined/null values to bottom
if (a == null) return 1;
if (b == null) return -1;
return a > b ? 1 : a < b ? -1 : 0;
},
},
];

Filtering

To allow users to filter a column, set the filterable property to true. You can configure how the filter matches values using filterFn.

Built-in Filter Modes

The filterFn property accepts any of these built-in mode names:

ModeDescription
autoAutomatically detect the best strategy
includesStringCase-insensitive substring match
includesStringSensitiveCase-sensitive substring match
equalsStringExact string equality
arrIncludesArray includes the filter value
arrIncludesAllArray includes all filter values
arrIncludesSomeArray includes some filter values
equalsStrict equality (===)
weakEqualsWeak equality (Object.is)
inNumberRangeValue falls within a numeric range

Custom Filter Functions

The DataTable ships with two built-in custom filter functions registered under the CustomFilterFns enum:

  • isInDateRange — Filters date columns by a [from, to] date range. The filter value is resolved as two Date objects.
  • isInPercentRange — Filters percentage columns by a [from, to] numeric range. Cell values (stored as decimals, e.g., 0.42) are multiplied by 100 before comparison.
import type {Column, CustomFilterFns} from 'koval-ui';

const columns: Column[] = [
{
id: 'createdAt',
name: 'Created',
accessorKey: 'createdAt',
columnType: 'date',
filterable: true,
filterFn: CustomFilterFns.isInDateRange,
},
{
id: 'growth',
name: 'Growth',
accessorKey: 'growth',
columnType: 'percentage',
filterable: true,
filterFn: CustomFilterFns.isInPercentRange,
},
];

You can also write your own custom filter function. A custom filterFn is passed the row, the column ID, and the current filter value.

Empty State

When active filters produce zero matching rows, the table displays a "No data to render" message with a Reset all filters button. Clicking it clears all active filters and restores the full dataset.

const columns = [
{
id: 'status',
name: 'Status',
accessorKey: 'status',
filterable: true,
filterFn: 'equalsString', // Requires an exact match
},
{
id: 'tags',
name: 'Tags',
accessorKey: 'tags',
filterable: true,
// Custom filtering function for an array of tags
filterFn: (row, columnId, filterValue) => {
const tags = row.getValue(columnId);
// Return true if the row should be included
return tags.some(tag => tag.toLowerCase().includes(filterValue.toLowerCase()));
},
},
];

Row Selection

You can add a checkbox column to enable row selection by passing selectable={true} to the <DataTable /> component. You can capture changes in selection by using the onRowSelect callback or controlling the selection externally via the rowSelection prop.

Result
Loading...
Live Editor
// import { DataTable } from 'koval-ui';

function Example() {
  const data = useMemo(() => generateData(3, 42), []);
  const [selection, setSelection] = React.useState({});

  const columns = useMemo(
    () => [
      {id: 'firstName', name: 'First Name', accessorKey: 'firstName'},
      {id: 'lastName', name: 'Last Name', accessorKey: 'firstName'},
      {id: 'role', name: 'Role', accessorKey: 'role'},
      {id: 'country', name: 'Country', accessorKey: 'address.country'},
    ],
    []
  );

  return (
    <div>
      <div style={{marginBottom: 16}}>
        Selected IDs: {Object.keys(selection).join(', ') || 'None'}
      </div>
      <DataTable
        tableData={data}
        columns={columns}
        selectable={true}
        onRowSelect={setSelection}
        tableHeight={300}
      />
    </div>
  );
}