---
name: data-grid
description: >
  MUI X DataGrid Premium with wcz-layout helpers. ChipInputCell for chip
  rendering in cells (getLabel for complex objects). EditableColumnHeader
  for pencil icon headers. RouterGridActionsCellItem for type-safe row
  action navigation. Column definitions with renderCell/renderHeader.
  Feed rows from useLiveQuery collection data. Activate when building
  data tables or grids.
type: composition
library: wcz-layout
library_version: "7.6.1"
requires:
  - tanstack-db-collections
  - ui-pages
sources:
  - "wcz-layout:src/components/data-grid/ChipInputCell.tsx"
  - "wcz-layout:src/components/data-grid/EditableColumnHeader.tsx"
  - "wcz-layout:src/components/router/RouterGridActionsCellItem.tsx"
---

# Data Grid

## Setup

Install `@mui/x-data-grid-premium` (peer dependency). Use `DataGridPremium`, not the community `DataGrid`.

## Core Patterns

### Basic data grid with collection data

```typescript
import { DataGridPremium, type GridColDef } from "@mui/x-data-grid-premium";
import { useLiveQuery } from "@tanstack/react-db";
import { todoCollection } from "~/lib/db/collections/todoCollection";
import type { Todo } from "~/schemas/todo";

const columns: GridColDef<Todo>[] = [
  { field: "name", headerName: "Name", flex: 1 },
  { field: "isCompleted", headerName: "Completed", type: "boolean", width: 120 },
  { field: "createdAt", headerName: "Created", type: "dateTime", width: 180 },
];

function TodoGrid() {
  const { data: todos } = useLiveQuery((query) =>
    query.from({ todos: todoCollection })
  );

  return (
    <DataGridPremium
      rows={todos}
      columns={columns}
      getRowId={(row) => row.id}
      autoHeight
    />
  );
}
```

### Row actions with RouterGridActionsCellItem

```typescript
import { RouterGridActionsCellItem } from "wcz-layout/components";
import EditIcon from "@mui/icons-material/Edit";
import DeleteIcon from "@mui/icons-material/Delete";
import type { GridColDef } from "@mui/x-data-grid-premium";

const columns: GridColDef<Todo>[] = [
  { field: "name", headerName: "Name", flex: 1 },
  {
    field: "actions",
    type: "actions",
    width: 100,
    getActions: (params) => [
      <RouterGridActionsCellItem
        key="edit"
        icon={<EditIcon />}
        label="Edit"
        to="/todos/edit/$id"
        params={{ id: params.row.id }}
      />,
      <RouterGridActionsCellItem
        key="delete"
        icon={<DeleteIcon />}
        label="Delete"
        onClick={() => handleDelete(params.row)}
        showInMenu
      />,
    ],
  },
];
```

`RouterGridActionsCellItem` wraps MUI's `GridActionsCellItem` with TanStack Router's `createLink` for type-safe SPA navigation.

### ChipInputCell for array/tag columns

```typescript
import { ChipInputCell } from "wcz-layout/components";
import type { GridColDef } from "@mui/x-data-grid-premium";

const columns: GridColDef<Todo>[] = [
  {
    field: "tags",
    headerName: "Tags",
    flex: 1,
    renderCell: (params) => <ChipInputCell params={params} />,
  },
];
```

For complex objects (not plain strings), provide `getLabel`:

```typescript
renderCell: (params) => (
  <ChipInputCell
    params={params}
    getLabel={(item) => item.displayName}
    slotProps={{ size: "small", color: "primary" }}
  />
),
```

`ChipInputCell` renders a horizontal scrollable stack of `Chip` components. It handles both scalar and array values automatically.

### EditableColumnHeader

```typescript
import { EditableColumnHeader } from "wcz-layout/components";

const columns: GridColDef<Todo>[] = [
  {
    field: "name",
    headerName: "Name",
    flex: 1,
    editable: true,
    renderHeader: (params) => <EditableColumnHeader {...params} />,
  },
];
```

Renders the column header text with a trailing pencil (Edit) icon to visually indicate editable columns. Purely visual — no interaction.

## Common Mistakes

### HIGH Using DataGrid instead of DataGridPremium

Wrong:

```typescript
import { DataGrid } from "@mui/x-data-grid";
```

Correct:

```typescript
import { DataGridPremium } from "@mui/x-data-grid-premium";
```

The library peer-depends on `@mui/x-data-grid-premium`. Using the community `DataGrid` loses Premium features like column grouping, row grouping, tree data, and aggregation.

Source: package.json peerDependencies

### HIGH Using GridActionsCellItem instead of RouterGridActionsCellItem

Wrong:

```typescript
import { GridActionsCellItem } from "@mui/x-data-grid-premium";

<GridActionsCellItem
  icon={<EditIcon />}
  label="Edit"
  onClick={() => navigate({ to: `/todos/${id}` })}
/>
```

Correct:

```typescript
import { RouterGridActionsCellItem } from "wcz-layout/components";

<RouterGridActionsCellItem
  icon={<EditIcon />}
  label="Edit"
  to="/todos/edit/$id"
  params={{ id }}
/>
```

Plain `GridActionsCellItem` doesn't support TanStack Router navigation. `RouterGridActionsCellItem` wraps it with `createLink` for type-safe routing without manual `onClick` + `navigate`.

Source: wcz-layout:src/components/router/RouterGridActionsCellItem.tsx

### MEDIUM Not passing getLabel to ChipInputCell for complex objects

Wrong:

```typescript
// params.value is Array<{ id: string; name: string }>
<ChipInputCell params={params} />
// Renders [object Object] chips
```

Correct:

```typescript
<ChipInputCell
  params={params}
  getLabel={(item) => item.name}
/>
```

`ChipInputCell` defaults `getLabel` to identity (value as-is). For objects, you must provide `getLabel` to extract the display string.

Source: wcz-layout:src/components/data-grid/ChipInputCell.tsx

### HIGH Tension: MUI component API vs. TanStack Router types

`RouterGridActionsCellItem` merges MUI grid action props with TanStack Router `LinkProps`. Use `to` + `params` for internal navigation. Using `onClick` + `navigate` bypasses the type-safe link and router prefetching.

See also: skills/ui-pages/SKILL.md § Common Mistakes

---

See also:

- skills/tanstack-db-collections/SKILL.md — Grid rows come from useLiveQuery results.
- skills/ui-pages/SKILL.md — Grid pages follow the same routing patterns.
