State
The grid holds one view state: sorting, filters, pagination, selection, column order / size / visibility / pinning, density, and more. You interact with it four ways — seed it, observe it, persist it, or read/drive it imperatively.
1. Seed with initialState
Give any slice a starting value. The grid owns it from there (the user can change it):
<DataTable
columns={columns}
data={rows}
enablePagination
enableColumnPinning
initialState={{
pagination: { pageIndex: 0, pageSize: 25 },
sorting: [{ id: 'name', desc: false }],
columnPinning: { left: ['name'], right: ['actions'] },
columnVisibility: { internalId: false },
density: 'compact',
}}
/>
2. Observe with onXChange
Watch a slice without owning it — see the full list in Events:
<DataTable columns={columns} data={rows}
onPaginationChange={(p) => console.log(p)}
onSelectionChange={(s) => setBanner(`${s.ids.length} selected`)} />
3. Control (a few features)
Most state is seed-and-observe (above). These are fully controllable with a value prop + callback:
| Feature | Value prop | Callback |
|---|---|---|
| Density | density | via enableDensitySelector |
| List view | listView | onListViewChange |
| Saved views | views, activeViewId | onViewsChange, onActiveViewChange |
In server mode you control the data by returning it from onFetchData.
4. Persist across reloads
Give the grid a stable stateKey and it remembers its view — pagination, sort, quick-search, column
filters, column order / width / visibility / pinning, and density — across reloads and remounts, with no
save/restore wiring:
<DataTable columns={columns} data={rows} stateKey="expenses" enablePagination enableColumnPinning />
It reads the snapshot into initialState on mount and writes (debounced) on change. Selection and row
expansion are excluded by default. Tune with persist:
<DataTable
stateKey="expenses"
persist={{
storage: 'session', // 'local' (default) | 'session' | custom Storage
include: ['sorting', 'pagination', 'columnPinning'], // whitelist (default: all but selection/expansion)
debounceMs: 500, // write throttle (default 300)
}}
/>
SSR-safe (no-ops without window). Keys are namespaced dt:<stateKey>. Clear a saved view with
clearPersistedState(stateKey, persist?) then remount. Saved views build on this — see
Saved views.
5. Read & drive imperatively
Read the live state or a slice through apiRef:
apiRef.current?.state.getTableState(); // the whole snapshot
apiRef.current?.state.getCurrentSorting();
apiRef.current?.state.getCurrentPagination();
apiRef.current?.state.getCurrentSelection();
apiRef.current?.state.getGlobalFilter();
Capture and restore just the column layout (order, size, pinning, visibility) — e.g. to your own storage:
const layout = apiRef.current?.layout.saveLayout(); // serializable snapshot
apiRef.current?.layout.restoreLayout(layout); // later
apiRef.current?.layout.reset(['columnOrder', 'columnSizing']); // back to initial