Column pinning
Pinned columns stick to the left or right edge while the rest of the grid scrolls horizontally. Turn it
on with enableColumnPinning.
Open the Columns panel in the toolbar (the ⊞ icon) — each column has pin-left / pin-right toggles next to its show/hide (eye) toggle. Click an active side again to unpin.
Enable it
<DataTable
columns={columns}
data={rows}
enableColumnPinning
enableColumnVisibility // surfaces the per-column pin toggles in the Columns panel
/>
enableColumnVisibility isn't required, but it's what renders the toolbar's pin UI. Without it you can
still pin via initialState or the apiRef.
Default pinned columns
When enableColumnPinning is on, the built-in selection (_selection) and expander
(_expanding) columns are pinned left automatically, so the checkbox and expand toggle stay visible
while scrolling. This merges with your own initialState.columnPinning — you only pin your columns,
the special ones stay left:
<DataTable
columns={columns}
data={rows}
enableColumnPinning
enableRowSelection
initialState={{
columnPinning: {
left: ['name'], // name stuck left (checkbox/expander auto-pinned before it)
right: ['actions'], // actions stuck right
},
}}
/>
To override the default, pin a special column yourself by its id — _selection (checkbox) or
_expanding (expander) — anywhere in left / right, and the automatic pin steps aside.
Pinned columns always render in column order. Within each side they follow the columns' order (or your drag-reorder), so a column that's last overall stays rightmost when pinned right, regardless of the order you pinned things in.
Imperative control
Drive pinning from your own UI through apiRef:
apiRef.current?.columnPinning.pinColumnLeft('name');
apiRef.current?.columnPinning.pinColumnRight('actions');
apiRef.current?.columnPinning.unpinColumn('name');
apiRef.current?.columnPinning.resetColumnPinning(); // back to the initial/default pinning
Listen for changes with the onColumnPinningChange prop.
Props
| Prop | Type | Description |
|---|---|---|
enableColumnPinning | boolean | Enable pinning + the toolbar pin toggles |
initialState.columnPinning | { left?: string[]; right?: string[] } | Initial (and reset) pinned columns |
onColumnPinningChange | (state) => void | Fires when pinning changes |
The detail-panel row from row expansion always spans the full width and is not affected by pinning.
Row pinning
Pin individual rows to the top and/or bottom so they stay visible while the rest scroll — pinned rows
survive sort, filter, and pagination. Turn it on with enableRowPinning. Below, Liam starts pinned to
the top and Zoe to the bottom; use the pushpin buttons in each row's actions to pin/unpin.
:::note Client data mode only
Row pinning re-hydrates pinned rows by id from the full local dataset, so it requires client-side data
(like footer aggregation). It's ignored under server pagination, where the engine
holds only one page. Top-level rows only — tree sub-rows can't be pinned. Give rows a stable
getRowId so pins track the right record.
:::
Pin rows
There's no built-in pin button (the grid has no per-row gutter), so pin via the imperative
apiRef or the exported createRowPinAction helper, which returns a
row action you spread into getRowActions:
import { DataTable, createRowPinAction } from '@ackplus/mui-tanstack-data-grid';
const apiRef = useRef(null);
<DataTable
apiRef={apiRef}
columns={columns}
data={rows}
enableRowPinning
initialState={{ rowPinning: { top: ['1'], bottom: ['8'] } }} // pre-pin by row id
getRowActions={(row) => [
createRowPinAction(apiRef, row, 'top'), // label/icon flip to "Unpin" when pinned
createRowPinAction(apiRef, row, 'bottom'),
]}
/>;
Or drive it directly:
apiRef.current?.rowPinning.pinRowTop('1');
apiRef.current?.rowPinning.pinRowBottom('8');
apiRef.current?.rowPinning.unpinRow('1');
apiRef.current?.rowPinning.resetRowPinning(); // back to initialState.rowPinning
apiRef.current?.rowPinning.getPinnedRowIds(); // { top: [...], bottom: [...] }
Listen with onRowPinningChange. Pins are included in saveLayout()/restoreLayout(); to persist them
across reloads add 'rowPinning' to persist.include and supply a stable getRowId.
Row-pinning props
| Prop | Type | Description |
|---|---|---|
enableRowPinning | boolean | Enable top/bottom row pinning (client data mode only) |
initialState.rowPinning | { top?: string[]; bottom?: string[] } | Initial (and reset) pinned row ids |
onRowPinningChange | (state) => void | Fires when row pinning changes |