Documentation
Concepts

Automatic widths

Flex columns that share the width the grid has left and follow it, and columns fitted to their content at the start, on a double click or from a button.

Besides the widths you give, the grid sizes columns two ways. A flex column takes its part of the width the other columns leave in the view, and follows the view as it grows and shrinks. A column fitted to its content gets the width of what its rendered cells show, measured by the grid. Which columns flex or fit, the buttons that fit them and what a reset means in your UI are yours.

Automatic widths
Gallery

The width on screen

A column's width on screen is the first of these, within its minWidth and maxWidth when it is resizable (a column that does not resize has no limits):

  1. the width a person set (columnWidths: a drag, a key, a fit, or your own column-widths.set, see Column resizing);
  2. its automatic width, when it has autoSize;
  3. its share of the view, when it has flex;
  4. its width.

A grid without flex or autoSize lays its columns out as before: nothing is measured, and a change of the view's width changes no column.

Flex columns

flex is a column's part of the width left in the view: what the root's width leaves once every other column has its own. Flex columns share it in proportion to their flex (flex: 2 takes two parts where flex: 1 takes one), each never below its width (its base) nor, when it is resizable, outside its minWidth and maxWidth. What one cannot take goes to the others. When nothing is left (the other columns already fill the view), a flex column is its base width, and the grid scrolls sideways as usual.

columns.tsx
const columns: Column<Person>[] = [
    { key: "id", name: "#", width: 64 },
    { key: "email", name: "Email", width: 220, flex: 2, resizable: true, minWidth: 160 },
    { key: "city", name: "City", width: 120, flex: 1, resizable: true, maxWidth: 240 },
];

The shares follow the root's size (the engine measures it, see Sizing the grid), the columns, their order and the widths a person set. When a share changes left of the view, the view stays on the column it shows first, as for a resize. flex belongs to columns: a group has none (columnsError refuses it), and its columns flex on their own.

A person resizing a flex column (a drag, a key, a fit) gives it a width of its own: it no longer flexes, and the other flex columns share what is left. A reset makes it flex again.

Fitting a column to its content

A fit measures this grid's own rendered cells of a column: its header cell and its body cells of loaded rows. Each one is laid out once at its content's width (its inline width set to max-content, read, and put back in the same task, so nothing paints in between); the column gets the widest, rounded up and, for a resizable column, held within its limits. A group's fit fits each of its resizable columns. Content placed out of the flow, such as an absolutely positioned resize handle, adds nothing.

Only rendered rows are measured

The grid renders the rows in view (and a few more), not the dataset: a fit measures those. A row scrolled into view later may be wider. To fit a column to all your data, work its width out yourself and set it with column-widths.set.

Three things fit a column, each with one column-widths.set holding the widths as they are plus the fitted ones, so a controlled parent answers once:

  • a double click on a column's handle (a group's handle fits each of its columns);
  • Enter on a focused handle, its keyboard equivalent (Keyboard and accessibility);
  • the engine's fit-columns action, for a button of yours: columnKeys names the columns (a group's key, its columns), and without it every rendered resizable column fits.
fit-all.tsx
const gridRef = useDataGridRef<Person>();

<button onClick={() => gridRef.current?.engine.run("fit-columns", {})}>Fit all</button>
<button onClick={() => gridRef.current?.engine.run("fit-columns", { columnKeys: ["email"] })}>
    Fit Email
</button>
<DataGrid.Root gridRef={gridRef} columns={columns} rows={people}>
    {/* … */}
</DataGrid.Root>

A fit resizes: it reaches only resizable columns, a column not rendered is not measured, and its width is one a person set, kept in columnWidths and told to onColumnWidthsChange, so a flex column stops flexing. A column fitted to the width it has without an override (its share or its automatic width as they would be without it, else its width) keeps no entry, as a drag or a key back to that width does.

Fitting once at the start

autoSize: true fits a column by itself once, after the first render that shows it with loaded rows, while the grid has a size (a grid in a hidden tab fits when it is laid out; a column whose cells measure nothing then keeps its width). Without resizable, it is its content's width as is, with no limits. That width is the grid's, not a person's: it is not in columnWidths, it is never reported, and a reset gives it back. With flex too, it is the column's base. It runs once per column key while the grid is on screen, and a new columns keeps it for the keys that stay; fit-columns (or a double click) measures again, as a width a person set.

columns.tsx
{ key: "name", name: "Name", width: 140, autoSize: true, resizable: true, minWidth: 120 }

Reset

column-widths.reset (or {} given to a controlled columnWidths) removes the widths a person set: a flex column flexes again and an autoSize column has its automatic width back. Your own "Reset widths" button is that command, or setWidths({}) when you keep the widths.

Reading the widths

engine.get("column-auto-widths") is the widths the grid gives columns itself, by key: the automatic widths and the flex shares in effect, never a width a person set. The engine's column-auto-widths event tells each change, for a readout of your own. get("column-width-by", { columnKey }) stays the model's width: the width a person set, else the column's own, not the width on screen. The width on screen is a handle's aria-valuenow and useColumnResizer's state.width, and a header cell's box (headerCellBox), all read from the view's column axis; aria-valuemin and aria-valuemax stay the column's limits.

A group with a column that flexes and does not resize

A group's handle shares a resize among the group's resizable columns. When the group also holds a flex column that does not resize, the handle cannot move that column, and its share changes as the others' widths change: the group ends off the pointer, and when that column alone takes what the view leaves, the group stays as wide as it was. Make such a column resizable too, or keep it out of a resizable group.

The options and the action

onnamedoes
columnflexits part of the width left in the view, never below its width nor past its maxWidth
columnautoSizefits its content once, when it first renders with loaded rows; the grid's width, never reported
enginerun("fit-columns", { columnKeys? })fits the named columns (a group's, its columns), or every rendered resizable one, in one column-widths.set
engineget("column-auto-widths"), column-auto-widths eventthe automatic widths and flex shares in effect, by key