DataTable
Sorting, search and column filters, pagination, checkbox row selection and column visibility on top of Table. useDataTable holds the state; DataTable renders it. No dependencies.
| Ada Lovelace | ada@northwind.dev | Owner | Active | 12 | |
| Grace Hopper | grace@northwind.dev | Admin | Active | 9 | |
| Alan Turing | alan@northwind.dev | Member | Active | 7 | |
| Katherine Johnson | katherine@northwind.dev | Member | Invited | 0 | |
| Margaret Hamilton | margaret@northwind.dev | Admin | Active | 15 |
import { Badge, DataTable, type DataTableColumn, useDataTable } from "@meridui/react";
const columns: DataTableColumn<Member>[] = [
{ id: "name", header: "Name", accessor: "name", sortable: true, hideable: false },
{ id: "email", header: "Email", accessor: "email" },
{ id: "role", header: "Role", accessor: "role", sortable: true },
{
id: "status",
header: "Status",
accessor: "status",
sortable: true,
cell: (row) => <Badge tone={row.status === "Active" ? "success" : "neutral"}>{row.status}</Badge>,
},
{ id: "projects", header: "Projects", accessor: "projects", sortable: true, align: "end" },
];
export function Members({ members }: { members: Member[] }) {
const table = useDataTable({ data: members, columns, defaultPageSize: 5 });
return (
<DataTable
table={table}
caption="Team members"
selectable
getRowLabel={(row) => row.name}
pageSizeOptions={[5, 10]}
/>
);
}Import
import { DataTable, useDataTable, type DataTableColumn } from "@meridui/react";Anatomy
const table = useDataTable({ data, columns });
<DataTable table={table} caption="…" />- Columns describe each field:
accessor(a key or a function) feeds sorting, search and the default cell;cellrenders anything else. - useDataTable is headless: it returns the current page of rows and every setter. Each piece of state works controlled or uncontrolled.
- DataTable renders a toolbar (search, Columns menu, your
toolbar), the Table with sortable headers and optional checkboxes, and a footer with the status line, a rows-per-page select and Pagination.
Examples
Column filters, default sort and hidden columns
Put your own filter controls in toolbar and drive them with setColumnFilter. A column's filterFn replaces the default "contains" match.
| Margaret Hamilton | Admin | Active | 15 |
| Ada Lovelace | Owner | Active | 12 |
| Grace Hopper | Admin | Active | 9 |
| Alan Turing | Member | Active | 7 |
| Barbara Liskov | Member | Active | 5 |
| Frances Allen | Member | Active | 4 |
| Linus Torvalds | Viewer | Active | 2 |
| Katherine Johnson | Member | Invited | 0 |
| Edsger Dijkstra | Viewer | Invited | 0 |
const table = useDataTable({
data: members,
columns,
defaultSort: { columnId: "projects", direction: "descending" },
defaultHiddenColumns: ["email"],
});
<DataTable
table={table}
caption="Team members by role"
toolbar={
<NativeSelect
size="sm"
aria-label="Role"
value={table.columnFilters.role ?? ""}
onChange={(event) => table.setColumnFilter("role", event.target.value)}
>
<option value="">All roles</option>
<option value="Owner">Owner</option>
{/* … */}
</NativeSelect>
}
/>Headless
Render your own markup from the same state when DataTable's layout does not fit, for example a list of cards on mobile.
const table = useDataTable({ data, columns });
// Everything DataTable renders is available for your own markup:
table.rows; // current page, filtered and sorted
table.toggleSort("name");
table.setGlobalFilter("ada");
table.setPage(2);
table.toggleRow(table.getRowId(row));
table.selectedRows; // across pages
table.toggleColumn("email", false);The hook works on the rows you pass in. For server-side sorting or paging, fetch with your own state and render Table directly.
API reference
DataTableColumn
| Prop | Type | Default | Description |
|---|---|---|---|
id | string | – | Unique column id. |
header | ReactNode | – | Header content. |
label | string | – | Plain-text name for the Columns menu. Defaults to header when it is a string. |
accessor | keyof Row | (row) => unknown | – | Value for sorting, search and the default cell. |
cell | (row) => ReactNode | – | Cell content. Defaults to the accessor value as text. |
sortable | boolean | false | Allow sorting by this column. |
sortFn | (a, b) => number | – | Custom ascending compare. Defaults to numeric, date and natural text order; empty values last. |
searchable | boolean | true with an accessor | Include the column in the global search. |
filterFn | (row, filter: string) => boolean | – | Custom per-column filter. |
hideable | boolean | true | List the column in the Columns menu. |
align | "start" | "center" | "end" | "start" | Text alignment; end for numbers. |
useDataTable
Every state has a controlled prop, a default… prop and an on…Change callback.
| Prop | Type | Default | Description |
|---|---|---|---|
data | Row[] | – | All rows. Memoise it. |
columns | DataTableColumn<Row>[] | – | Column definitions. |
getRowId | (row, index) => string | row.id ?? index | Stable id for selection. |
sort / defaultSort / onSortChange | { columnId, direction } | null | – | One sorted column at a time. |
globalFilter / defaultGlobalFilter / onGlobalFilterChange | string | – | Search text; every word must match some searchable column. |
columnFilters / defaultColumnFilters / onColumnFiltersChange | Record<string, string> | – | Per-column filters. |
page / defaultPage / onPageChange | number | 1 | Current page, 1-based. |
pageSize / defaultPageSize / onPageSizeChange | number | 10 | Rows per page. |
selection / defaultSelection / onSelectionChange | string[] | – | Selected row ids. |
hiddenColumns / defaultHiddenColumns / onHiddenColumnsChange | string[] | – | Hidden column ids. |
It returns rows, filteredRows, visibleColumns, page, pageCount, pageSelection ("all" | "some" | "none"), selectedRows and the setters toggleSort, setGlobalFilter, setColumnFilter, setPage, setPageSize, toggleRow, togglePage, clearSelection and toggleColumn. Changing a filter or the page size returns to page 1.
DataTable
Accepts every Table prop.
| Prop | Type | Default | Description |
|---|---|---|---|
table | DataTableInstance<Row> | – | State from useDataTable. |
caption | ReactNode | – | Names the table; visually hidden unless showCaption. |
showCaption | boolean | false | Show the caption above the table. |
selectable | boolean | false | Add a checkbox column. |
getRowLabel | (row) => string | – | Names a row in its checkbox label. Defaults to the first visible column. |
searchable | boolean | true | Show the search box. |
columnToggle | boolean | true if any column is hideable | Show the Columns menu. |
paginated | boolean | true with more than one page | Show pagination. |
pageSizeOptions | number[] | – | Offer a rows-per-page select. |
toolbar | ReactNode | – | Extra controls in the toolbar. |
emptyState | ReactNode | labels.empty | Shown when no row matches. |
labels | Partial<DataTableLabels> | – | Every visible and screen-reader string; defaults are English. |
rootClassName | string | – | Class name for the outer wrapper. |
DataTableLabels: search, searchPlaceholder, columns, selectPage, selectRow(rowLabel), selected(count, total), results(count), empty, rowsPerPage, pagination, previousPage, nextPage, page(n).
Styling
| Class | Element |
|---|---|
.mrd-data-table | Wrapper |
.mrd-data-table__toolbar, .mrd-data-table__search, .mrd-data-table__columns | Toolbar parts |
.mrd-data-table__select | Checkbox cells |
.mrd-data-table__empty | Empty row cell |
.mrd-data-table__footer, .mrd-data-table__status, .mrd-data-table__page-size | Footer parts |
Rows, headers and cells are the Table's own (.mrd-table__*); selected rows get data-selected.
Accessibility
The table keeps native table semantics with a <caption>. Sortable headers are buttons inside <th aria-sort>; every checkbox has a name ("Select Ada Lovelace", "Select all rows on this page") and the header checkbox shows the mixed state. The status line is a polite live region that announces the number of results after a search and the selection count; the search box is described by it.
| Key | Action |
|---|---|
| Tab | Move through search, Columns, sort buttons, checkboxes and pagination. |
| EnterSpace | On a sort button: cycle ascending, descending, unsorted. |
| Space | On a checkbox: select or clear the row (or the page). |
Guidelines
Do
Right-align numbers (`align: "end"`) and keep the name column always visible (`hideable: false`).
Avoid
Load thousands of rows into the hook. Page and sort on the server and render Table instead.
Related
- Table: the markup DataTable builds on.
- Data tables pattern: layout and bulk actions.
- Pagination: the footer control.