# Source: https://mui.com/x/react-data-grid/column-definition.md

# Data Grid - Column definition

Define your columns.

The columns are defined with the `columns` prop which has the type `GridColDef[]`.

`field` is the only required property since it's the column identifier. It's also used to match with `GridRowModel` values.

```ts
interface GridColDef {
  /**
   * The column identifier. It's used to match with [[GridRowModel]] values.
   */
  field: string;
  …
}
```

```tsx
import Box from '@mui/material/Box';
import { DataGrid } from '@mui/x-data-grid';

export default function BasicColumnsGrid() {
  return (
    <Box sx={{ height: 250, width: '100%' }}>
      <DataGrid
        columns={[{ field: 'username' }, { field: 'age' }]}
        rows={[
          {
            id: 1,
            username: '@MUI',
            age: 20,
          },
        ]}
      />
    </Box>
  );
}

```

:::warning
The `columns` prop should keep the same reference between two renders.
The columns are designed to be definitions, to never change once the component is mounted.
Otherwise, you take the risk of losing elements like column width or order.
You can create the array outside the render function or memoize it.
:::

## Providing content

By default, the Data Grid uses the field of a column to get its value.
For instance, the column with field `name` will render the value stored in `row.name`.
But for some columns, it can be useful to manually get and format the value to render.

### Value getter

:::warning
Note that the signature of `valueGetter` has changed in v7 – see the [migration guide](https://mui.com/x/migration/migration-data-grid-v6/) for details.

If you're using v6, please use the [v6 documentation](https://v6.mui.com/x/react-data-grid/column-definition/#value-getter).
:::

Sometimes a column might not have a desired value.
You can use the `valueGetter` attribute of `GridColDef` to:

1. Transform the value

   ```tsx
   const columns: GridColDef[] = [
     {
       field: 'taxRate',
       valueGetter: (value) => {
         if (!value) {
           return value;
         }
         // Convert the decimal value to a percentage
         return value * 100;
       },
     },
   ];
   ```

2. Render a combination of different fields

   ```tsx
   const columns: GridColDef[] = [
     {
       field: 'fullName',
       valueGetter: (value, row) => {
         return `${row.firstName || ''} ${row.lastName || ''}`;
       },
     },
   ];
   ```

3. Derive a value from a complex value

   ```tsx
   const columns: GridColDef[] = [
     {
       field: 'profit',
       valueGetter: (value, row) => {
         if (!row.gross || !row.costs) {
           return null;
         }
         return row.gross - row.costs;
       },
     },
   ];
   ```

The value returned by `valueGetter` is used for:

- Filtering
- Sorting
- Rendering (unless enhanced further by [`valueFormatter`](/x/react-data-grid/column-definition/#value-formatter) or [`renderCell`](/x/react-data-grid/column-definition/#rendering-cells))

```tsx
import Box from '@mui/material/Box';
import { DataGrid, GridColDef, GridValueGetter } from '@mui/x-data-grid';

const rows = [
  { id: 1, lastName: 'Snow', firstName: 'Jon' },
  { id: 2, lastName: 'Lannister', firstName: 'Cersei' },
  { id: 3, lastName: 'Lannister', firstName: 'Jaime' },
  { id: 4, lastName: 'Stark', firstName: 'Arya' },
  { id: 5, lastName: 'Targaryen', firstName: 'Daenerys' },
];

const getFullName: GridValueGetter<(typeof rows)[number], unknown> = (
  value,
  row,
) => {
  return `${row.firstName || ''} ${row.lastName || ''}`;
};

const columns: GridColDef[] = [
  { field: 'firstName', headerName: 'First name', width: 130 },
  { field: 'lastName', headerName: 'Last name', width: 130 },
  {
    field: 'fullName',
    headerName: 'Full name',
    width: 160,
    valueGetter: getFullName,
  },
];

export default function ValueGetterGrid() {
  return (
    <Box sx={{ height: 400, width: '100%' }}>
      <DataGrid rows={rows} columns={columns} />
    </Box>
  );
}

```

:::warning
`valueGetter` can be called for autogenerated rows, which are created when features like [row grouping](/x/react-data-grid/row-grouping/) or [aggregation](/x/react-data-grid/aggregation/) are used.
Read more in the [handling autogenerated rows](/x/react-data-grid/column-definition/#autogenerated-rows) section.
:::

:::warning
[Row grouping](/x/react-data-grid/row-grouping/) uses the [`groupingValueGetter()`](/x/react-data-grid/row-grouping/#using-groupingvaluegetter-for-complex-grouping-value) instead of `valueGetter` to get the value for the grouping.
The value passed to the `groupingValueGetter()` is the raw row value (`row[field]`) even if the column definition has a `valueGetter` defined.
:::

### Value formatter

:::warning
Note that the signature of `valueFormatter` has changed in v7 – see the [migration guide](https://mui.com/x/migration/migration-data-grid-v6/) for details.

If you're using v6, please use the [v6 documentation](https://v6.mui.com/x/react-data-grid/column-definition/#value-formatter).
:::

The value formatter lets you convert the value before displaying it.
Common use cases include converting a JavaScript `Date` object to a date string or a `Number` into a formatted number (for example "1,000.50").

Note, that the value returned by `valueFormatter` is only used for rendering purposes.
Filtering and sorting are based on the raw value (`row[field]`) or the value returned by [`valueGetter`](/x/react-data-grid/column-definition/#value-getter).

In the following demo, `valueGetter` is used to convert the tax rate (for example `0.2`) to a decimal value (for example `20`),
and `valueFormatter` is used to display it as a percentage (for example `20%`).

```tsx
import { DataGrid } from '@mui/x-data-grid';

const rows = [
  {
    id: 1,
    taxRate: 0.1,
  },
  {
    id: 2,
    taxRate: 0.2,
  },
  {
    id: 3,
    taxRate: 0.3,
  },
];

export default function ValueFormatterGrid() {
  return (
    <div style={{ height: 300, width: '100%' }}>
      <DataGrid
        rows={rows}
        columns={[
          {
            type: 'number',
            field: 'taxRate',
            headerName: 'Tax Rate',
            width: 150,
            valueGetter: (value) => {
              if (!value) {
                return value;
              }
              return value * 100;
            },
            valueFormatter: (value?: number) => {
              if (value == null) {
                return '';
              }
              return `${value.toLocaleString()} %`;
            },
          },
        ]}
      />
    </div>
  );
}

```

:::warning
`valueFormatter` can be called for autogenerated rows, which are created when features like [row grouping](/x/react-data-grid/row-grouping/) or [aggregation](/x/react-data-grid/aggregation/) are used.
Read more in the [handling autogenerated rows](/x/react-data-grid/column-definition/#autogenerated-rows) section.
:::

## Rendering cells

By default, the Data Grid renders the value as a string in the cell.
It resolves the rendered output in the following order:

1. `renderCell() => ReactElement`
2. `valueFormatter() => string`
3. `valueGetter() => string`
4. `row[field]`

The `renderCell` method of the column definitions is similar to `valueFormatter`.
However, it trades the ability to only render in a cell in exchange for letting you return a React node (instead of a string).

```tsx
const columns: GridColDef[] = [
  {
    field: 'date',
    headerName: 'Year',
    renderCell: (params: GridRenderCellParams<any, Date>) => (
      <strong>
        {params.value.getFullYear()}
        <Button
          variant="contained"
          size="small"
          style={{ marginLeft: 16 }}
          tabIndex={params.hasFocus ? 0 : -1}
        >
          Open
        </Button>
      </strong>
    ),
  },
];
```

```tsx
import * as React from 'react';
import Button from '@mui/material/Button';
import { DataGrid, GridColDef, GridRenderCellParams } from '@mui/x-data-grid';
import { TouchRippleActions } from '@mui/material/ButtonBase/TouchRipple';

function RenderDate(props: GridRenderCellParams<any, Date>) {
  const { hasFocus, value } = props;
  const buttonElement = React.useRef<HTMLButtonElement>(null);
  const rippleRef = React.useRef<TouchRippleActions>(null);

  React.useLayoutEffect(() => {
    if (hasFocus) {
      const input = buttonElement.current!.querySelector('input');
      input?.focus();
    } else if (rippleRef.current) {
      // Only available in @mui/material v5.4.1 or later
      rippleRef.current.stop({} as any);
    }
  }, [hasFocus]);

  return (
    <strong>
      {value?.getFullYear() ?? ''}
      <Button
        ref={buttonElement}
        touchRippleRef={rippleRef}
        variant="contained"
        size="small"
        style={{ marginLeft: 16 }}
        // Remove button from tab sequence when cell does not have focus
        tabIndex={hasFocus ? 0 : -1}
        onKeyDown={(event: React.KeyboardEvent) => {
          if (event.key === ' ') {
            // Prevent key navigation when focus is on button
            event.stopPropagation();
          }
        }}
      >
        Open
      </Button>
    </strong>
  );
}

const columns: GridColDef[] = [
  {
    field: 'date',
    headerName: 'Year',
    width: 150,
    renderCell: RenderDate,
  },
];

const rows = [
  {
    id: 1,
    date: new Date(1979, 0, 1),
  },
  {
    id: 2,
    date: new Date(1984, 1, 1),
  },
  {
    id: 3,
    date: new Date(1992, 2, 1),
  },
];

export default function RenderCellGrid() {
  return (
    <div style={{ height: 300, width: '100%' }}>
      <DataGrid rows={rows} columns={columns} />
    </div>
  );
}

```

:::warning
When using `renderCell`, if the type of the value returned by `valueGetter` does not correspond to the column's `type`, you should:

- handle [sorting](/x/react-data-grid/sorting/#custom-comparator) by providing `sortComparator` to the column.
- set a `valueFormatter` providing a representation for the value to be used when [exporting](/x/react-data-grid/export/#exported-cells) the data.

:::

:::warning
`renderCell` can be called for autogenerated rows, which are created when features like [row grouping](/x/react-data-grid/row-grouping/) or [aggregation](/x/react-data-grid/aggregation/) are used.
Read more in the [handling autogenerated rows](/x/react-data-grid/column-definition/#autogenerated-rows) section.
:::

### Styling cells

You can check the [styling cells](/x/react-data-grid/style/#styling-cells) section for more information.

### Making accessible cells

Cell content should not be in the tab sequence except if cell is focused.
You can check the [tab sequence](/x/react-data-grid/accessibility/#tab-sequence) section for more information.

### Using hooks inside a renderer

The `renderCell` property is a function that returns a React node, not a React component.

If you want to use React hooks inside your renderer, you should wrap them inside a component.

```tsx
// ❌ Not valid
const column = {
  // ...other properties,
  renderCell: () => {
    const [count, setCount] = React.useState(0);

    return (
      <Button onClick={() => setCount((prev) => prev + 1)}>{count} click(s)</Button>
    );
  },
};

// ✅ Valid
const CountButton = () => {
  const [count, setCount] = React.useState(0);

  return (
    <Button onClick={() => setCount((prev) => prev + 1)}>{count} click(s)</Button>
  );
};

const column = {
  // ...other properties,
  renderCell: () => <CountButton />,
};
```

:::warning
Because of pagination and virtualization, cells can be unmounted when scrolling or switching pages.
The internal state of the component returned by renderCell will be lost.

If you want the cell information to persist, you should save it either in the Data Grid state or in the Data Grid parent.
:::

### Expand cell renderer

By default, the Data Grid cuts the content of a cell and renders an ellipsis if the content of the cell does not fit in the cell.
As a workaround, you can create a cell renderer that lets users see the full content of the cell in the Data Grid.

```tsx
import * as React from 'react';
import Box from '@mui/material/Box';
import Typography from '@mui/material/Typography';
import Paper from '@mui/material/Paper';
import Popper from '@mui/material/Popper';
import { DataGrid, GridColDef, GridRenderCellParams } from '@mui/x-data-grid';

interface GridCellExpandProps {
  value: string;
  width: number;
}

function isOverflown(element: Element): boolean {
  return (
    element.scrollHeight > element.clientHeight ||
    element.scrollWidth > element.clientWidth
  );
}

const GridCellExpand = React.memo(function GridCellExpand(
  props: GridCellExpandProps,
) {
  const { width, value } = props;
  const wrapper = React.useRef<HTMLDivElement | null>(null);
  const cellDiv = React.useRef(null);
  const cellValue = React.useRef(null);
  const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
  const [showFullCell, setShowFullCell] = React.useState(false);
  const [showPopper, setShowPopper] = React.useState(false);

  const handleMouseEnter = () => {
    const isCurrentlyOverflown = isOverflown(cellValue.current!);
    setShowPopper(isCurrentlyOverflown);
    setAnchorEl(cellDiv.current);
    setShowFullCell(true);
  };

  const handleMouseLeave = () => {
    setShowFullCell(false);
  };

  React.useEffect(() => {
    if (!showFullCell) {
      return undefined;
    }

    function handleKeyDown(nativeEvent: KeyboardEvent) {
      if (nativeEvent.key === 'Escape') {
        setShowFullCell(false);
      }
    }

    document.addEventListener('keydown', handleKeyDown);

    return () => {
      document.removeEventListener('keydown', handleKeyDown);
    };
  }, [setShowFullCell, showFullCell]);

  return (
    <Box
      ref={wrapper}
      onMouseEnter={handleMouseEnter}
      onMouseLeave={handleMouseLeave}
      sx={{
        alignItems: 'center',
        lineHeight: '24px',
        width: '100%',
        height: '100%',
        position: 'relative',
        display: 'flex',
      }}
    >
      <Box
        ref={cellDiv}
        sx={{
          height: '100%',
          width,
          display: 'block',
          position: 'absolute',
          top: 0,
        }}
      />
      <Box
        ref={cellValue}
        sx={{ whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}
      >
        {value}
      </Box>
      {showPopper && (
        <Popper
          open={showFullCell && anchorEl !== null}
          anchorEl={anchorEl}
          style={{ width, marginLeft: -17 }}
        >
          <Paper
            elevation={1}
            style={{ minHeight: wrapper.current!.offsetHeight - 3 }}
          >
            <Typography variant="body2" style={{ padding: 8 }}>
              {value}
            </Typography>
          </Paper>
        </Popper>
      )}
    </Box>
  );
});

function renderCellExpand(params: GridRenderCellParams<any, string>) {
  return (
    <GridCellExpand value={params.value || ''} width={params.colDef.computedWidth} />
  );
}

const columns: GridColDef[] = [
  { field: 'col1', headerName: 'Column 1', width: 80, renderCell: renderCellExpand },
  {
    field: 'col2',
    headerName: 'Column 2',
    width: 100,
    renderCell: renderCellExpand,
  },
  {
    field: 'col3',
    headerName: 'Column 3',
    width: 150,
    renderCell: renderCellExpand,
  },
];
const rows: any = [
  {
    id: 1,
    col1: 'Hello',
    col2: 'World',
    col3: 'In publishing and graphic design, Lorem ipsum is a placeholder text commonly used.',
  },
  {
    id: 2,
    col1: 'DataGridPro',
    col2: 'is Awesome',
    col3: 'In publishing and graphic design, Lorem ipsum is a placeholder text or a typeface without relying on meaningful content. Lorem ipsum may be used as a placeholder before final copy is available.',
  },
  {
    id: 3,
    col1: 'MUI',
    col2: 'is Amazing',
    col3: 'Lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document or a typeface without relying on meaningful content. Lorem ipsum may be used as a placeholder before final copy is available.',
  },
  {
    id: 4,
    col1: 'Hello',
    col2: 'World',
    col3: 'In publishing and graphic design, Lorem ipsum is a placeholder text commonly used to demonstrate the visual form.',
  },
  {
    id: 5,
    col1: 'DataGridPro',
    col2: 'is Awesome',
    col3: 'Typeface without relying on meaningful content. Lorem ipsum may be used as a placeholder before final copy is available.',
  },
  {
    id: 6,
    col1: 'MUI',
    col2: 'is Amazing',
    col3: 'Lorem ipsum may be used as a placeholder before final copy is available.',
  },
];

export default function RenderExpandCellGrid() {
  return (
    <div style={{ height: 300, width: '100%' }}>
      <DataGrid rows={rows} columns={columns} />
    </div>
  );
}

```

:::warning
Because of pagination and virtualization, cells can be unmounted when scrolling or switching pages.
The internal state of the component returned by `renderCell` will be lost.

If you want to persist cell information, you should save it either in the Data Grid parent or in the row model.
Updating the row will rerender the row and so call renderCell with updated params.
:::

## Column types

To facilitate the configuration of the columns, some column types are predefined.
By default, columns are assumed to hold strings, so the default column string type will be applied. As a result, column sorting will use the string comparator, and the column content will be aligned to the left side of the cell. Some column types require that their value have a specific type.

The following are the native column types with their required value types:

| Column type          | Value type                 |
| :------------------- | :------------------------- |
| `'string'` (default) | `string`                   |
| `'longText'`         | `string`                   |
| `'number'`           | `number`                   |
| `'date'`             | `Date() object`            |
| `'dateTime'`         | `Date() object`            |
| `'boolean'`          | `boolean`                  |
| `'singleSelect'`     | A value in `.valueOptions` |
| `'actions'`          | Not applicable             |

```tsx
import * as React from 'react';
import {
  DataGrid,
  GridActionsCellItem,
  GridRowId,
  GridColDef,
  GridActionsCell,
  GridRenderCellParams,
} from '@mui/x-data-grid';
import DeleteIcon from '@mui/icons-material/Delete';
import SecurityIcon from '@mui/icons-material/Security';
import FileCopyIcon from '@mui/icons-material/FileCopy';
import { randomCreatedDate, randomUpdatedDate } from '@mui/x-data-grid-generator';

const initialRows = [
  {
    id: 1,
    name: 'Damien',
    age: 25,
    dateCreated: randomCreatedDate(),
    lastLogin: randomUpdatedDate(),
    isAdmin: true,
    country: 'Spain',
    discount: '',
    bio: 'Damien is a software engineer with 5 years of experience in web development. He specializes in React and Node.js.',
  },
  {
    id: 2,
    name: 'Nicolas',
    age: 36,
    dateCreated: randomCreatedDate(),
    lastLogin: randomUpdatedDate(),
    isAdmin: false,
    country: 'France',
    discount: '',
    bio: 'Nicolas is a product manager who loves building user-centric products. He has led multiple successful product launches.',
  },
  {
    id: 3,
    name: 'Kate',
    age: 19,
    dateCreated: randomCreatedDate(),
    lastLogin: randomUpdatedDate(),
    isAdmin: false,
    country: 'Brazil',
    discount: 'junior',
    bio: 'Kate is a junior developer passionate about learning new technologies. She is currently focused on frontend development.',
  },
];

type Row = (typeof initialRows)[number];

interface ActionHandlers {
  deleteUser: (id: GridRowId) => void;
  toggleAdmin: (id: GridRowId) => void;
  duplicateUser: (id: GridRowId) => void;
}

const ActionHandlersContext = React.createContext<ActionHandlers>({
  deleteUser: () => {},
  toggleAdmin: () => {},
  duplicateUser: () => {},
});

function ActionsCell(props: GridRenderCellParams) {
  const { deleteUser, toggleAdmin, duplicateUser } =
    React.useContext(ActionHandlersContext);

  return (
    <GridActionsCell {...props}>
      <GridActionsCellItem
        icon={<DeleteIcon />}
        label="Delete"
        onClick={() => deleteUser(props.id)}
      />
      <GridActionsCellItem
        icon={<SecurityIcon />}
        label="Toggle Admin"
        onClick={() => toggleAdmin(props.id)}
        showInMenu
      />
      <GridActionsCellItem
        icon={<FileCopyIcon />}
        label="Duplicate User"
        onClick={() => duplicateUser(props.id)}
        showInMenu
      />
    </GridActionsCell>
  );
}

const columns: GridColDef<Row>[] = [
  { field: 'name', type: 'string' },
  {
    field: 'bio',
    type: 'longText',
    width: 200,
  },
  { field: 'age', type: 'number' },
  { field: 'dateCreated', type: 'date', width: 130 },
  { field: 'lastLogin', type: 'dateTime', width: 180 },
  { field: 'isAdmin', type: 'boolean', width: 120 },
  {
    field: 'country',
    type: 'singleSelect',
    width: 120,
    valueOptions: [
      'Bulgaria',
      'Netherlands',
      'France',
      'United Kingdom',
      'Spain',
      'Brazil',
    ],
  },
  {
    field: 'discount',
    type: 'singleSelect',
    width: 120,
    editable: true,
    valueOptions: ({ row }) => {
      if (row === undefined) {
        return ['EU-resident', 'junior'];
      }
      const options: string[] = [];
      if (!['United Kingdom', 'Brazil'].includes(row.country)) {
        options.push('EU-resident');
      }
      if (row.age < 27) {
        options.push('junior');
      }
      return options;
    },
  },
  {
    field: 'actions',
    type: 'actions',
    width: 80,
    renderCell: (params) => <ActionsCell {...params} />,
  },
];

export default function ColumnTypesGrid() {
  const [rows, setRows] = React.useState<Row[]>(initialRows);

  const deleteUser = React.useCallback((id: GridRowId) => {
    setTimeout(() => {
      setRows((prevRows) => prevRows.filter((row) => row.id !== id));
    });
  }, []);

  const toggleAdmin = React.useCallback((id: GridRowId) => {
    setRows((prevRows) =>
      prevRows.map((row) =>
        row.id === id ? { ...row, isAdmin: !row.isAdmin } : row,
      ),
    );
  }, []);

  const duplicateUser = React.useCallback((id: GridRowId) => {
    setRows((prevRows) => {
      const rowToDuplicate = prevRows.find((row) => row.id === id)!;
      return [...prevRows, { ...rowToDuplicate, id: Date.now() }];
    });
  }, []);

  const actionHandlers = React.useMemo<ActionHandlers>(
    () => ({
      deleteUser,
      toggleAdmin,
      duplicateUser,
    }),
    [deleteUser, toggleAdmin, duplicateUser],
  );

  return (
    <div style={{ height: 300, width: '100%' }}>
      <ActionHandlersContext.Provider value={actionHandlers}>
        <DataGrid columns={columns} rows={rows} />
      </ActionHandlersContext.Provider>
    </div>
  );
}

```

### Long text keyboard interactions

The `'longText'` column type supports keyboard interactions to expand, collapse, and edit content when the cell is focused via click or keyboard navigation.

View mode (expand button focused):

- <kbd class="key">Space</kbd> – Toggle popup
- <kbd class="key">Escape</kbd> – Close popup

Edit mode (textarea focused):

- <kbd><kbd class="key">Shift</kbd>+<kbd class="key">Enter</kbd></kbd> – Insert newline
- <kbd class="key">Enter</kbd> – Commit changes
- <kbd class="key">Escape</kbd> – Cancel editing

### Converting types

Default methods, such as filtering and sorting, assume that the type of the values will match the type of the column specified in `type`.
For example, values of column with `type: 'dateTime'` are expecting to be stored as a `Date()` objects.
If for any reason, your data type is not the correct one, you can use `valueGetter` to parse the value to the correct type.

```tsx
{
  field: 'lastLogin',
  type: 'dateTime',
  valueGetter: (value) => value && new Date(value),
}
```

### Special properties

To use most of the column types, you only need to define the `type` property in your column definition.
However, some types require additional properties to be set to make them work correctly:

#### Single select

If the column type is `'singleSelect'`, you also need to set the `valueOptions` property in the respective column definition. These values are options used for filtering and editing.

```tsx
{
  field: 'country',
  type: 'singleSelect',
  valueOptions: ['United Kingdom', 'Spain', 'Brazil']
}
```

:::warning
When using objects values for `valueOptions` you need to provide the `value` and `label` attributes for each option.
However, you can customize which attribute is used as value and label by using `getOptionValue` and `getOptionLabel`, respectively.

```tsx
// Without getOptionValue and getOptionLabel
{
  valueOptions: [
    { value: 'BR', label: 'Brazil' },
    { value: 'FR', label: 'France' }
  ]
}

// With getOptionValue and getOptionLabel
{
  getOptionValue: (value: any) => value.code,
  getOptionLabel: (value: any) => value.name,
  valueOptions: [
    { code: 'BR', name: 'Brazil' },
    { code: 'FR', name: 'France' }
  ]
}
```

:::

#### Actions

If the column type is `'actions'`, you need to provide a `renderCell` function that renders a `GridActionsCell` component with `GridActionsCellItem` elements as children.
You can add the `showInMenu` prop on the `GridActionsCellItem` elements to signal the Data Grid to group these actions inside a row menu.

```tsx
{
  field: 'actions',
  type: 'actions',
  renderCell: (params) => (
    <GridActionsCell {...params}>
      <GridActionsCellItem icon={...} onClick={...} label="Delete" />
      <GridActionsCellItem icon={...} onClick={...} label="Print" showInMenu />
    </GridActionsCell>
  )
}
```

<!-- TODO(v9): remove the warning below -->

:::warning
This is the recommended way to define actions in the column definition starting from v8.19.0.
The `getActions` method that returned an array of actions is deprecated, and will be removed in a future version.
:::

By default, actions shown in the menu will close the menu on click.
But in some cases, you might want to keep the menu open after clicking an action.
You can achieve this by setting the `closeMenuOnClick` prop to `false`.

In the following example, the "Delete" action opens a confirmation dialog and therefore needs to keep the menu mounted:

```tsx
import * as React from 'react';
import {
  DataGrid,
  GridActionsCellItem,
  GridRowId,
  GridColDef,
  GridActionsCell,
  GridRenderCellParams,
} from '@mui/x-data-grid';
import DeleteIcon from '@mui/icons-material/Delete';
import { randomUserName } from '@mui/x-data-grid-generator';
import Dialog from '@mui/material/Dialog';
import DialogTitle from '@mui/material/DialogTitle';
import DialogContent from '@mui/material/DialogContent';
import DialogContentText from '@mui/material/DialogContentText';
import DialogActions from '@mui/material/DialogActions';
import Button from '@mui/material/Button';

const initialRows = [
  { id: 1, name: randomUserName() },
  { id: 2, name: randomUserName() },
  { id: 3, name: randomUserName() },
];

type Row = (typeof initialRows)[number];

const ActionHandlersContext = React.createContext<
  ((id: GridRowId) => void) | undefined
>(undefined);

function ActionsCell(props: GridRenderCellParams) {
  const setActionRowId = React.useContext(ActionHandlersContext);

  if (!setActionRowId) {
    throw new Error('ActionHandlersContext is empty');
  }

  return (
    <GridActionsCell {...props}>
      <GridActionsCellItem
        label="Delete"
        showInMenu
        icon={<DeleteIcon />}
        onClick={() => setActionRowId(props.id)}
        closeMenuOnClick={false}
      />
    </GridActionsCell>
  );
}

const columns: GridColDef<Row>[] = [
  { field: 'name', type: 'string' },
  {
    field: 'actions',
    type: 'actions',
    width: 80,
    renderCell: (params) => <ActionsCell {...params} />,
  },
];

export default function ActionsWithModalGrid() {
  const [rows, setRows] = React.useState<Row[]>(initialRows);
  const [actionRowId, setActionRowId] = React.useState<GridRowId | null>(null);

  const deleteActiveRow = React.useCallback(
    (rowId: GridRowId) =>
      setRows((prevRows) => prevRows.filter((row) => row.id !== rowId)),
    [],
  );

  const handleCloseDialog = React.useCallback(() => {
    setActionRowId(null);
  }, []);

  const handleConfirmDelete = React.useCallback(() => {
    deleteActiveRow(actionRowId!);
    handleCloseDialog();
  }, [actionRowId, deleteActiveRow, handleCloseDialog]);

  return (
    <div style={{ height: 300, width: '100%' }}>
      <ActionHandlersContext.Provider value={setActionRowId}>
        <DataGrid columns={columns} rows={rows} />
      </ActionHandlersContext.Provider>
      <Dialog
        open={actionRowId !== null}
        onClose={handleCloseDialog}
        aria-labelledby="alert-dialog-title"
        aria-describedby="alert-dialog-description"
      >
        <DialogTitle id="alert-dialog-title">Delete this user?</DialogTitle>
        <DialogContent>
          <DialogContentText id="alert-dialog-description">
            This action cannot be undone.
          </DialogContentText>
        </DialogContent>
        <DialogActions>
          <Button onClick={handleCloseDialog}>Cancel</Button>
          <Button onClick={handleConfirmDelete} color="warning" autoFocus>
            Delete
          </Button>
        </DialogActions>
      </Dialog>
    </div>
  );
}

```

:::success
In the example above, the React Context API is used to pass the action handlers to the `ActionsCell` component.
This is a recommended pattern to keep the column definitions stable.
:::

### Custom column types

Please refer to the [custom columns](/x/react-data-grid/custom-columns/) page for documentation and integration examples.

## Autogenerated rows

Some features like [row grouping](/x/react-data-grid/row-grouping/) or [aggregation](/x/react-data-grid/aggregation/) create autogenerated rows.
These rows also call functions like `valueGetter`, `valueFormatter` and `renderCell`, and that can cause issues if you're not expecting it because the `row` parameter will be an empty object and the `value` parameter will be `undefined`.
If we take for example the movie dataset, you can detect autogenerated rows using `isAutogeneratedRow()`:

```tsx
{
  field: 'title',
  valueGetter: (value, row) => {
    if (isAutogeneratedRow(row)) {
      return '[this is an autogenerated row]';
    }
    return `title: ${value}`;
  },
}
```

```tsx
import {
  DataGridPremium,
  GRID_ROW_GROUPING_SINGLE_GROUPING_FIELD,
  GridColDef,
  isAutogeneratedRow,
  useGridApiRef,
  useKeepGroupedColumnsHidden,
} from '@mui/x-data-grid-premium';
import { useMovieData } from '@mui/x-data-grid-generator';

const columns: GridColDef[] = [
  { field: GRID_ROW_GROUPING_SINGLE_GROUPING_FIELD, width: 200 },
  { field: 'company', width: 200 },
  {
    field: 'title',
    minWidth: 250,
    cellClassName: 'highlighted',
    valueGetter: (value, row) => {
      if (isAutogeneratedRow(row)) {
        return '[this is an autogenerated row]';
      }
      return `title: ${value}`;
    },
  },
  { field: 'year' },
];

export default function AutogeneratedRows() {
  const { rows } = useMovieData();
  const apiRef = useGridApiRef();

  const initialState = useKeepGroupedColumnsHidden({
    apiRef,
    initialState: {
      rowGrouping: {
        model: ['company'],
      },
    },
  });

  return (
    <div style={{ height: 400, width: '100%' }}>
      <DataGridPremium
        rows={rows}
        columns={columns}
        apiRef={apiRef}
        initialState={initialState}
        defaultGroupingExpansionDepth={-1}
        sx={{
          '.MuiDataGrid-cell:not(.highlighted)': {
            color: '#999',
          },
        }}
      />
    </div>
  );
}

```

## Selectors

### Visible columns

Those selectors do not take into account hidden columns.

{{"component": "modules/components/SelectorsDocs.js", "category": "Visible Columns"}}

### Defined columns

Those selectors consider all the defined columns, including hidden ones.

{{"component": "modules/components/SelectorsDocs.js", "category": "Columns"}}

More information about the selectors and how to use them on the [dedicated page](/x/react-data-grid/state/#access-the-state).

## API

- [DataGrid](/x/api/data-grid/data-grid/)
- [DataGridPro](/x/api/data-grid/data-grid-pro/)
- [DataGridPremium](/x/api/data-grid/data-grid-premium/)