Skip to main content

DataGrid

Feature-rich data grid with search, column filters, sort, pagination, selection, column resize/reorder/hide, and CSV export.

Quick start
import { DataGrid, type DataGridColumn } from "mates-ui";

DataGrid({
  columnsAtom: colsAtom,   // atom<DataGridColumn<T>[]>
  columns:     colsAtom(), // current read
  rows:        myData,
  rowKey:      (r) => r.id,
});

Full-featured DataGrid

import { html, atom, nothing, renderApp } from 'mates';
import { Avatar, Badge, Chip, DataGrid, Text, dataGridExportCsv, filterDataGridRows, sortDataGridRows } from 'mates-ui';

const USERS = Array.from({ length: 50 }, (_, i) => ({
id: String(i + 1),
name: "User " + (i + 1),
email: "user" + (i + 1) + "@example.com",
role: (["Admin", "Editor", "Viewer"])[i % 3],
status: (["active", "inactive", "pending"])[i % 3],
joined: "Jan 2024",
}));

const STATUS_BADGE = { active: "success", inactive: "neutral", pending: "warning" };

const BASE_COLUMNS = [
{ id: "name", header: "Name", sortable: true, filterable: true, minWidth: 192, accessor: (r) => r.name,
cell: (r) => html`<div style="display:flex;align-items:center;gap:var(--md-space-2);">${Avatar({ name: r.name, size: "sm" })}<span style="font-weight:600;">${r.name}</span></div>` },
{ id: "email", header: "Email", sortable: true, filterable: true, minWidth: 224, accessor: (r) => r.email,
cell: (r) => html`<span style="color:var(--md-color-text-muted);">${r.email}</span>` },
{ id: "role", header: "Role", sortable: true, filterable: true, minWidth: 128, accessor: (r) => r.role,
cell: (r) => Chip(r.role, { variant: "assist" }) },
{ id: "status", header: "Status", sortable: true, minWidth: 112, align: "center", accessor: (r) => r.status,
cell: (r) => Badge(r.status, { variant: STATUS_BADGE[r.status] }) },
{ id: "joined", header: "Joined", sortable: true, minWidth: 128, accessor: (r) => r.joined,
cell: (r) => html`<span style="color:var(--md-color-text-muted);font-size:var(--md-text-xs);">${r.joined}</span>` },
];

const App = () => {
const colsAtom = atom([...BASE_COLUMNS]);
const sortBy = atom("name");
const sortDir = atom("asc");
const sel = atom([]);
const page = atom(1);
const pageSize = atom(8);
const globalFilter = atom("");
const colFilterModel = atom([]);

DataGrid

import { DataGrid, type DataGridColumn, type DataGridColumnFilter } from "mates-ui";
Prop Type Default Description
attr AttrMap HTML attributes on the root element.
bodyHeight string Fixed height for virtualized scroll.
classes string | string[] Extra root classes — joined and appended after built-in classes.
columnFilterModel DataGridColumnFilter[] Per-column structured filters.
columnsAtom AtomType<DataGridColumn<T>[]> Reactive atom for column definitions. Enables resize, reorder, hide to write back automatically.
data DataAttrMap Root `data-*` map (`testId` → `data-testid`). `testId` prop / `attr` win over `data`.
enableColumnHiding boolean Show/hide columns via toolbar panel.
enableColumnReorder boolean Drag headers to reorder.
enableColumnResize boolean Drag column edges to resize.
enablePagination boolean Show footer pagination controls.
globalFilter string Global search value (toolbar search field).
id string Root `id` shorthand (`attr.id` wins). Form native-control `id` props are unchanged.
on OnEventMap DOM event handlers on the root element.
rowKey* (row: T) => string Unique key extractor per row.
rows* T[] Array of data rows.
selectable boolean Enable row checkboxes.
showExportCsv boolean Show CSV export button in toolbar.
stickyHeader boolean Sticky column headers.
style StyleMap Inline CSS on the root element.
variant "default" | "striped" | "bordered" Visual style.

Caveats

  • Pass the same atom to both columnsAtom and columns — columnsAtom is the write-back target, columns is the read snapshot used for the current render.
  • Reset page to 1 in onGlobalFilterChange and onColumnFilterModelChange to avoid empty pages after filtering.
  • dataGridExportCsv exports already-filtered and sorted rows — call filterDataGridRows + sortDataGridRows before it.