

<Preview name="DataGridCombinedScenario" />

## Overview [#overview]

`DataGrid` renders records as a passive table, navigable list, selectable list,
or spreadsheet. The consumer owns the records and workflows; DataGrid provides
the table presentation and interaction.

Create the table, supply stable records and column definitions, and compose
product controls and cell content from Cubitt primitives.

Large row models are virtualized automatically, while smaller tables remain in
normal document flow. DataGrid does not provide product toolbars, formulas,
requests, or persistence. Standard header menus reflect the actions enabled for
each column.

## Usage [#usage]

```tsx
import {
  createDataGridColumnHelper,
  DataGrid,
  useDataGridTable,
} from "@tilt-legal/cubitt-components/data-grid";
```

Define stable column definitions outside the component that owns the table:

```tsx
type Matter = {
  id: string;
  name: string;
  status: string;
};

const columnHelper = createDataGridColumnHelper<Matter>();
const columns = columnHelper.columns([
  columnHelper.accessor("name", {
    flex: 2,
    header: "Matter",
    meta: { rowHeader: true },
    minSize: 160,
  }),
  columnHelper.accessor("status", {
    header: "Status",
  }),
]);
```

Create the table from consumer-owned records, then pass it to DataGrid:

```tsx
export function MattersGrid({
  matters,
  onOpenMatter,
}: {
  matters: Matter[];
  onOpenMatter: (matterId: string) => void;
}) {
  const table = useDataGridTable(
    {
      columns,
      data: matters,
      getRowId: (row) => row.id,
    },
    () => null,
  );

  return (
    <DataGrid
      interaction="row-navigation"
      onRowActivate={({ row }) => onOpenMatter(row.original.id)}
      table={table}
    >
      <DataGrid.Viewport aria-label="Matters" />
    </DataGrid>
  );
}
```

The second `useDataGridTable` argument selects state used by the component that
creates the table. Use `() => null` when it does not render from table state.
See [Shared State](/components/data-grid/shared-state) for shared state and
subscriptions.

Import general TanStack types such as `ColumnDef`, `GroupingState`, `Row`, and
`Cell` directly from `@tanstack/react-table`.

## Ownership [#ownership]

| Owner    | Responsibilities                                                                                                                                                                             |
| -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Consumer | Records, queries, stable IDs, column definitions, product controls, editor drafts, validation, permissions, persistence, clipboard formats, fill algorithms, and loading or error decisions. |
| Cubitt   | Table rendering and styling, keyboard and pointer interaction, accessibility, editing presentation, expansion presentation, fill requests, and overlays.                                     |

Use table methods to change table state and application callbacks to change
records.

## Interaction patterns [#interaction-patterns]

DataGrid is passive by default. Without `interaction`, rows and cells remain
display-only while links, buttons, menus, sorting, grouping, and optional
selection checkboxes continue to work normally.

Choose a preset when the grid should own pointer and keyboard gestures. The
preset provides a common starting composition; lower-level row, column, and
cell features can refine it.

| Pattern                                                | Configure                                                    | Use when                                                         |
| ------------------------------------------------------ | ------------------------------------------------------------ | ---------------------------------------------------------------- |
| [Row Navigation](/components/data-grid/row-navigation) | Set `interaction="row-navigation"` and pass `onRowActivate`. | Clicking or pressing Enter on a row opens or acts on its record. |
| [Row Selection](/components/data-grid/row-selection)   | Set `interaction="row-selection"`.                           | Clicking a row should toggle its selection state.                |
| [Spreadsheet](/components/data-grid/spreadsheet)       | Set `interaction="spreadsheet"`.                             | Cells need spreadsheet focus, range selection, fill, or editing. |

Interactive cell content keeps its normal behavior in every preset. A consumer
handler may call `event.preventDefault()` to reserve a gesture. When a custom
cell composition overlaps the root preset, that specific handler takes
precedence instead of causing both behaviors.

## Content before the table [#content-before-the-table]

Compose summaries, product controls, or other leading content as children of
`DataGrid.Viewport`. The content and table share one scroll container, so the
leading content scrolls away before the table header sticks to the viewport.
It remains aligned to the viewport when the table scrolls horizontally.

<Preview className="min-h-[32rem]" name="DataGridLeadingContentScenario" padded="horizontal" />

```tsx
<DataGrid table={table}>
  <DataGrid.Viewport aria-label="Line items">
    <ReviewOverviewCards />
    <LineItemsToolbar />
  </DataGrid.Viewport>
</DataGrid>
```

For a fixed-height sticky toolbar, offset the table header by the same height so
the two sticky elements stack instead of overlap.

```tsx
<DataGrid.Viewport
  aria-label="Line items"
  className="[--data-grid-sticky-header-offset:3.5rem]"
>
  <ReviewOverviewCards />
  <LineItemsToolbar className="sticky top-0 z-4 h-14 py-2" />
</DataGrid.Viewport>
```

The viewport exposes scroll overflow attributes. Use `data-overflow-y-start`
to style a sibling header when content has scrolled above the grid, without a
scroll handler:

```tsx
<section className="group/records flex min-h-0 flex-col">
  <header className="border-b border-transparent group-has-[[data-slot=data-grid-viewport][data-overflow-y-start]]/records:border-current">
    <Breadcrumbs />
  </header>
  <DataGrid table={table}>
    <DataGrid.Viewport aria-label="Line items" className="max-h-96" />
  </DataGrid>
</section>
```

## Appearance [#appearance]

DataGrid uses row separators by default. Set `variant="bordered"` for cell
borders. The appearance is independent of the selected interaction preset.

The bordered appearance is intended mainly for full-bleed tables that share
their outer edges with a parent page, card, or inset. The parent surface owns
the perimeter border and any corner radius.

<Preview className="overflow-hidden" name="DataGridBorderedScenario" padded="false" />

```tsx
<DataGrid interaction="row-selection" table={table} variant="bordered">
  <DataGrid.Viewport aria-label="Work plan" />
</DataGrid>
```

## Guides [#guides]

### Structure [#structure]

<Cards>
  <Card title="Columns" href="/components/data-grid/columns">
    Configure sizing, resizing, ordering, visibility, pinning, grouped headers,
    sorting, and filtering.
  </Card>

  <Card title="Rows" href="/components/data-grid/rows">
    Configure grouping, hierarchy, expanded details, and row pinning.
  </Card>
</Cards>

### Interactions [#interactions]

<Cards>
  <Card title="Row Navigation" href="/components/data-grid/row-navigation">
    Activate records through pointer clicks and row-first keyboard navigation.
  </Card>

  <Card title="Row Selection" href="/components/data-grid/row-selection">
    Compose row-click selection and the optional checkbox column.
  </Card>

  <Card title="Spreadsheet" href="/components/data-grid/spreadsheet">
    Use cell selection, fill, focus, keyboard navigation, and editing.
  </Card>
</Cards>

### Advanced [#advanced]

<Cards>
  <Card title="Cell Spanning" href="/components/data-grid/cell-spanning">
    Merge adjacent cells across rows or columns independently of interaction.
  </Card>

  <Card title="Pivoting" href="/components/data-grid/pivoting">
    Compose Rows, Columns, and Values, including summaries and totals.
  </Card>

  <Card title="Virtualization" href="/components/data-grid/virtualization">
    Control row virtualization and inspect its resolved geometry.
  </Card>

  <Card title="Saved Views" href="/components/data-grid/saved-views">
    Capture, restore, and persist durable grid configuration.
  </Card>

  <Card title="Shared State" href="/components/data-grid/shared-state">
    Share a specific state slice with another product surface.
  </Card>
</Cards>

## API Reference [#api-reference]

This page lists the root composition APIs. Feature-specific options, methods,
and primitives are documented at the end of the related guide.

### DataGrid [#datagrid]

| Prop                | Type                                                     | Default                        | Description                                                                                                           |
| ------------------- | -------------------------------------------------------- | ------------------------------ | --------------------------------------------------------------------------------------------------------------------- |
| `table`             | `DataGridTable<TData>`                                   | -                              | Consumer-created table returned by `useDataGridTable`. Required.                                                      |
| `children`          | `ReactNode`                                              | -                              | Usually a `DataGrid.Viewport` and optional overlay. Required.                                                         |
| `interaction`       | `"row-navigation" \| "row-selection" \| "spreadsheet"`   | `undefined`                    | Selects a root interaction preset. Omit it for a passive table.                                                       |
| `variant`           | `"default" \| "bordered"`                                | `"default"`                    | Selects the row-separated or full-bleed cell-border appearance.                                                       |
| `onRowActivate`     | `(context: DataGridRowActivationContext<TData>) => void` | -                              | Handles row activation. Required for row navigation and paired with `getRowCanActivate` for a row-selection override. |
| `getRowCanActivate` | `(row) => boolean`                                       | `() => true` in row navigation | Limits eligible navigation rows or selects rows that override row selection with activation.                          |
| `onFillRequest`     | `(request: DataGridFillRequest) => void`                 | -                              | Reports a requested source and target range in the spreadsheet preset without mutating records.                       |
| `className`         | `string`                                                 | -                              | Additional layout classes for the root container.                                                                     |

### DataGrid.Viewport [#datagridviewport]

The viewport is the scroll, focus, and table render surface. It requires either
`aria-label` or `aria-labelledby`. Passive DataGrids expose table semantics,
interactive flat rows expose a grid, and interactive hierarchical rows expose
a treegrid. See [Virtualization](/components/data-grid/virtualization) for
geometry and diagnostics.

| Prop                      | Type                     | Default | Description                                                                       |
| ------------------------- | ------------------------ | ------- | --------------------------------------------------------------------------------- |
| `aria-label`              | `string`                 | -       | Accessible table or grid name. Mutually exclusive with `aria-labelledby`.         |
| `aria-labelledby`         | `string`                 | -       | ID of the element naming the table or grid. Mutually exclusive with `aria-label`. |
| `children`                | `ReactNode`              | -       | Leading content rendered before the table inside the shared scroll viewport.      |
| `renderExpandedContent`   | `(context) => ReactNode` | -       | Consumer-rendered detail content for expanded rows.                               |
| `renderGroupedRowContent` | `(context) => ReactNode` | -       | Consumer-rendered full-width content for synthetic grouped rows.                  |
| `className`               | `string`                 | -       | Additional layout classes for the scroll viewport.                                |

Standard `div` props, including `style` and `onScroll`, apply to the scrolling
element. DataGrid uses Cubitt's `ScrollArea` with native browser scrollbars; do not add another
scroll wrapper.

| Data attribute                                 | Present when                                                              |
| ---------------------------------------------- | ------------------------------------------------------------------------- |
| `data-has-overflow-x`, `data-has-overflow-y`   | Content overflows on that axis.                                           |
| `data-overflow-x-start`, `data-overflow-x-end` | More content is available toward the horizontal start or end.             |
| `data-overflow-y-start`, `data-overflow-y-end` | More content is available above or below the viewport.                    |
| `data-scrolling`                               | Scrolling is active, rather than indicating a persistent scroll position. |

### DataGrid.Overlay [#datagridoverlay]

Layout for consumer-composed loading, empty, or error content. The consumer
decides when the overlay appears and supplies its copy and actions. Pass an
empty data array with an empty-state overlay so row counts remain accurate.

| Prop        | Type        | Default | Description                |
| ----------- | ----------- | ------- | -------------------------- |
| `children`  | `ReactNode` | -       | Overlay content.           |
| `className` | `string`    | -       | Additional layout classes. |

`DataGrid.Overlay` also accepts standard `div` props.

### Table helpers [#table-helpers]

| Export                       | Signature or type                                         | Description                                                                                                 |
| ---------------------------- | --------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| `useDataGridTable`           | `(options, selector?) => DataGridTable<TData, TSelected>` | Creates the table instance used by DataGrid and optionally selects the state exposed through `table.state`. |
| `createDataGridColumnHelper` | `createDataGridColumnHelper<TData>()`                     | Creates a typed helper for accessor, display, and grouped column definitions.                               |
| `DataGridColumnDef`          | `DataGridColumnDef<TData>`                                | Type for stable column-definition collections passed across consumer boundaries.                            |
| `DataGridTable`              | `DataGridTable<TData, TSelected>`                         | Type of the table instance returned by `useDataGridTable`.                                                  |
| `DataGridFeatures`           | Feature-set type                                          | Type used when a consumer boundary needs to name DataGrid rows, columns, or cells.                          |
