Docs Platform

DataGrid

DataGrid keeps row data and interaction state separate from DOM. The machine coordinates the children, the headless layer exposes one closed anatomy, and the styled layer supplies the visual treatment.

Live machine

The shipped gridMachine coordinator with its sort and pagination children enabled — events route to children by type, and each child keeps its own context.

grid · live via createService

page 1 of 12

States · walked from the machine

ready

Transitions from here · click to send

Context

{
  "columns": null,
  "sort": {
    "value": "ready",
    "context": {
      "mode": "uncontrolled",
      "sortModel": [
        {
          "key": "p95",
          "dir": "asc"
        }
      ]
    }
  },
  "filter": null,
  "selection": null,
  "focus": null,
  "edit": null,
  "groups": null,
  "pagination": {
    "value": "ready",
    "context": {
      "mode": "uncontrolled",
      "page": 1,
      "total": 120,
      "pageSize": 10,
      "siblingCount": 1,
      "pageCount": 12,
      "range": [
        1,
        2,
        "…",
        12
      ]
    }
  }
}

Log · newest first

No events yet — use the component above or send a transition.

Explore the layers

Machine: @grassroot/ui-machines/grid. Headless: @grassroot/ui-headless-{react,vue,solid,svelte,angular}/grid. Styled: @grassroot/ui-{react,vue,solid,svelte,angular} with the DataGrid root. The core projection and vanilla mount are in @grassroot/ui-headless-core/grid.

Anatomy

The closed anatomy is deliberately small and shared by every adapter:

root · scroller · content · header · header-row · header-cell
column-resize-handle · column-menu-trigger · body · row · group-row
group-toggle · cell · cell-editor · select-all · row-checkbox
quick-filter-input · column-filter-panel · overlay · live-region

Every connection is scoped with data-scope="grid" and a stable data-part. Rows and cells expose deterministic ids, while the root reports its row and column counts through ARIA. The live region carries announcements without becoming part of the visual row flow.

Behaviour contract

The coordinator owns eight child services: columns, sort, filter, selection, focus, edit, groups, and pagination. It synchronizes the canonical ordered row ids and produces one projection for renderers. Updates are pure state transitions, so pointer, keyboard, clipboard, and framework event handlers can share the same commands.

The core project also provides transactional updates, CSV export, column visibility and order, grouped rows, filter predicates, stable sorting, and diagnostics for duplicate or missing row and column ids.

Virtualization

computeVirtualWindow computes a bounded overscanned window from scroll offset, viewport height, estimated row height, and total rows. The adapters render the window inside a scroller/content pair; they do not materialize all rows. Virtualizer observes row sizes with ResizeObserver, exposes a subscription for framework updates, and is inert when no observer is available in SSR.

Pointer column resizing, pointer row dragging, and clipboard copy/paste are independent core helpers. They can be adopted without changing the grid state machine or the public anatomy.

Remote rows

rowModel selects the row source independently from display mode: use client for local rows, server for replacement pages or windows, and infinite for sequential virtualized blocks. Remote sources receive serializable query intent and an abort signal through the shared Runner bridge; a later sort, filter, page, or window replaces stale work.

<DataGrid
  rowModel="server"
  dataSource={(query, { signal }) => loadRows(query, signal)}
  initialRowData={seedRows}
  initialRowCount={seedTotal}
  mode="paginated"
/>

// query: { rowModel, operation, sortModel, filterModel, page?, range?, cursor? }
// result: { rows, total?, nextCursor? }

Seed rows are optional SSR first paint. Selection remains keyed by row id across refreshes, Retry repeats the last remote descriptor, and a remote result is never locally sorted, filtered, or paginated again.

Framework adapters

React, Vue, Solid, Svelte, and Angular expose the same grid projection and part names. Headless adapters attach the coordinator lifecycle to their framework mount/unmount hooks; styled adapters add the existing class and token conventions. The browser contract covers sorting, selection, filtering, edit mode, keyboard focus, and the virtualized row boundary for each adapter.