

## Overview [#overview]

DataGrid manages feature state internally by default. Keep that default when
only the table and its controls need a state slice.

The second argument to `useDataGridTable` selects the state that should rerender
the component creating the table. Use `() => null` when that component does not
render from table state, and use `table.Subscribe` where a narrow reactive value
is displayed.

Pass a stable external atom through `atoms` only when another surface must read
or share a specific slice. Give each slice one owner rather than mirroring it
into React state. `useDataGridFilters` already follows this pattern for filters;
use [Saved Views](/components/data-grid/saved-views) when several durable slices
need to be captured and restored together.

## Example [#example]

Create one stable atom for the shared slice. Controls continue to use the normal
table methods.

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview name="DataGridExternalStateScenario" />
  </Tab>

  <Tab value="Code">
    ```tsx
    const grouping = useCreateAtom<GroupingState>([]);

    const table = useDataGridTable(
      {
        atoms: { grouping },
        columns,
        data: matters,
        getRowId: (row) => row.id,
      },
      () => null,
    );

    <table.Subscribe source={grouping}>
      {(value) => (
        <Toolbar.Count>Grouped: {value.join(", ") || "none"}</Toolbar.Count>
      )}
    </table.Subscribe>

    <DataGrid table={table}>
      <DataGrid.Viewport aria-label="Matters with shared grouping state" />
    </DataGrid>
    ```
  </Tab>
</Tabs>

## Custom cell rendering [#custom-cell-rendering]

Cell and table objects are mutable handles, not immutable render snapshots.
Do not memoize a renderer solely by `cell` or `table` identity: permissions,
selection, and other state can change without either handle changing.

For expensive content, pass the values it uses into a memoized child. Leave
capability reads such as `cell.getCanEdit()` in the outer renderer so option
changes still reach the child. Keep columns and data stable when unchanged.

If the table owner opts out of a state slice, a custom cell reading that slice
must subscribe explicitly. For example, use TanStack's `Subscribe` with the
table's row-selection atom and pass the selected boolean to a display component:

```tsx
import { Subscribe } from '@tanstack/react-table';
import { memo } from 'react';

const NameContent = memo(function NameContent({
  name,
  selected,
  editable,
}: {
  name: string;
  selected: boolean;
  editable: boolean;
}) {
  return <span>{name}{selected ? ' (selected)' : ''}{editable ? ' (editable)' : ''}</span>;
});

columnHelper.accessor('name', {
  cell: ({ cell, row, table, getValue }) => (
    <Subscribe
      source={table.atoms.rowSelection}
      selector={() => row.getIsSelected()}
    >
      {(selected) => (
        <NameContent
          name={getValue()}
          selected={selected}
          editable={cell.getCanEdit()}
        />
      )}
    </Subscribe>
  ),
});
```

This isolates expensive display work without guessing which properties an
arbitrary renderer might read. React context used inside the memoized subtree
continues to update normally.

## API Reference [#api-reference]

### Reactive state [#reactive-state]

| API                | Signature or type                                                | Description                                                                                                                                  |
| ------------------ | ---------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| `useDataGridTable` | `(options, selector?) => DataGridTable<TData, TSelected>`        | Creates the table and exposes the selector result through `table.state`. Omitting the selector subscribes the owner to the full table state. |
| `table.Subscribe`  | React component with `source?`, `selector?`, and render children | Subscribes a narrow subtree to the full table state or one supplied atom. A full-state subscription requires a selector.                     |
| `options.atoms`    | Partial map of writable state atoms                              | Gives selected table-state slices a stable external owner.                                                                                   |
| `table.atoms`      | Readonly map of state atoms                                      | Reads or subscribes to the current value of a specific state slice.                                                                          |

The selector result is reflected in `DataGridTable<TData, TSelected>` when a
table instance crosses a typed component boundary.
