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.