# Using Context Menus

> InfiniteTable DataGrid allows you to easily configure context menus for any row and cell in the table and for the whole table body.

Canonical page: https://infinite-table.com/docs/learn/context-menus/using-context-menus

The easiest way to configure a context menu is to provide the [`getCellContextMenuItems`](https://infinite-table.com/docs/reference/infinite-table-props.md#getCellContextMenuItems) callback function and use it to return the menu items you want to show in the context menu.

```tsx
const getCellContextMenuItems = ({ column, value }) => {
  if (column.id === 'currency') {
    return [
      {
        label: `Convert ${value}`,
        key: 'currency-convert',
      },
    ];
  }

  if (column.id === 'age') {
    return null;
  }

  return [
    {
      label: `Welcome ${value}`,
      key: 'hi',
    },
  ];
};

<DataSource<Developer> data={data} primaryKey="id">
  <InfiniteTable<Developer>
    getCellContextMenuItems={getCellContextMenuItems}
    columns={columns}
  />
</DataSource>;
```

**Example: Using context menus**

Right-click any cell in the table to see the custom context menu.

```ts
import {
  InfiniteTable,
  DataSource,
  InfiniteTablePropColumns,
} from '@infinite-table/infinite-react';

import * as React from 'react';

type Developer = {
  id: number;
  firstName: string;
  lastName: string;
  country: string;
  city: string;
  currency: string;
  preferredLanguage: string;
  stack: string;
  canDesign: 'yes' | 'no';
  hobby: string;
  salary: number;
  age: number;
};

const dataSource = () => {
  return fetch(process.env.NEXT_PUBLIC_BASE_URL + '/developers100')
    .then((r) => r.json())
    .then((data: Developer[]) => data);
};

const columns: InfiniteTablePropColumns<Developer> = {
  stack: {
    field: 'stack',

    header: 'Stack',
  },
  firstName: {
    field: 'firstName',
    header: 'Name',
  },
  age: {
    field: 'age',
    header: 'Age',
  },
  hobby: {
    field: 'hobby',
    header: 'Hobby',
  },
  preferredLanguage: {
    header: 'Language',
    field: 'preferredLanguage',
  },
};

export default function App() {
  return (
    <>
      <DataSource<Developer> primaryKey="id" data={dataSource}>
        <InfiniteTable<Developer>
          debugId="basic-cells-context-menu-example"
          columns={columns}
          getCellContextMenuItems={({ data, column }) => {
            return [
              {
                key: 'hello',
                label: `Hello, ${data?.lastName} ${data?.firstName}`,
                onClick: () => {
                  alert(`Hello, ${data?.lastName} ${data?.firstName}`);
                },
              },
              {
                key: 'col',
                label: `Current clicked column: ${column.header}`,
              },
              {
                key: 'learn',
                label: `Learn`,
                menu: {
                  items: [
                    {
                      key: 'backend',
                      label: 'Backend',
                      onClick: () => {
                        alert(
                          `Learn Backend, ${data?.lastName} ${data?.firstName}`,
                        );
                      },
                    },
                    {
                      key: 'frontend',
                      label: 'Frontend',
                      onClick: () => {
                        alert(
                          `Learn Frontend, ${data?.lastName} ${data?.firstName}`,
                        );
                      },
                    },
                  ],
                },
              },
            ];
          }}
        />
      </DataSource>
    </>
  );
}
```

The [`getCellContextMenuItems`](https://infinite-table.com/docs/reference/infinite-table-props.md#getCellContextMenuItems) function can return one of the following:

- `null` - no custom context menu will be displayed, the default context menu will be shown (default event behavior not prevented)
- `[]` - an empty array - no custom context menu will be displayed, but the default context menu is not shown - the default event behavior is prevented
- `Array<MenuItem>` - an array of menu items to be displayed in the context menu - each `MenuItem` should have:
  - a unique `key` property,
  - a `label` property with the value to display in the menu cell - it's called `label` because this is the name of the default column in the context menu
  - an optional `onClick` callback function to handle the click event on the menu item.

In addition, if you need to configure the context menu to have other columns rather than the default column (named `label`), you can do so by returning an object with `columns` and `items`:

```tsx
const getCellContextMenuItems = () => {
  return {
    columns: [{ name: 'label' }, { name: 'lcon' }],
    items: [
      {
        label: 'Welcome',
        icon: '👋',
        key: 'hi',
        onAction: () => {
          // do something
        },
        hideMenuOnAction: true,
      },
      {
        label: 'Convert',
        icon: '🔁',
        key: 'convert',
      },
    ],
  };
};
```

**Example: Customising columns in the context menu**

Right-click any cell in the table to see a context menu with multiple columns (`icon`, `label` and `description`).

```ts
import {
  InfiniteTable,
  DataSource,
  InfiniteTablePropColumns,
} from '@infinite-table/infinite-react';

import * as React from 'react';

type Developer = {
  id: number;
  firstName: string;
  lastName: string;
  country: string;
  city: string;
  currency: string;
  preferredLanguage: string;
  stack: string;
  canDesign: 'yes' | 'no';
  hobby: string;
  salary: number;
  age: number;
};

const dataSource = () => {
  return fetch(process.env.NEXT_PUBLIC_BASE_URL + '/developers100')
    .then((r) => r.json())
    .then((data: Developer[]) => data);
};

const columns: InfiniteTablePropColumns<Developer> = {
  stack: {
    field: 'stack',
    header: 'Stack',
  },
  firstName: {
    field: 'firstName',
    header: 'Name',
  },
  age: {
    field: 'age',
    header: 'Age',
  },
  hobby: {
    field: 'hobby',
    header: 'Hobby',
  },
  preferredLanguage: {
    header: 'Language',
    field: 'preferredLanguage',
  },
};

export default function App() {
  return (
    <>
      <DataSource<Developer> primaryKey="id" data={dataSource}>
        <InfiniteTable<Developer>
          debugId="custom-columns-context-menu-example"
          columns={columns}
          columnDefaultEditable
          getCellContextMenuItems={({ data, column }) => {
            const columns = [
              { name: 'icon' },
              { name: 'label' },
              { name: 'description' },
            ];
            return {
              columns,
              items: [
                {
                  key: 'hello',
                  icon: '👋',
                  label: `Hello, ${data?.lastName} ${data?.firstName}`,
                  description: `This is a description for ${data?.lastName}`,
                  onClick: () => {
                    alert(`Hello, ${data?.lastName} ${data?.firstName}`);
                  },
                },
                {
                  key: 'col',
                  icon: '🙌',
                  label: `Column: ${column.header}`,
                  description: `Current clicked column: ${column.header}`,
                },
                {
                  key: 'learn',
                  icon: '📚',
                  label: `Learn`,
                  description: `Learn more about ${data?.preferredLanguage}`,
                  menu: {
                    columns,
                    items: [
                      {
                        key: 'backend',
                        label: 'Backend',
                        icon: '👨‍💻',
                        description: 'In the Backend',
                        onClick: () => {
                          alert(
                            `Learn Backend, ${data?.lastName} ${data?.firstName}`,
                          );
                        },
                      },
                      {
                        key: 'frontend',
                        label: 'Frontend',
                        icon: '👨‍💻',
                        description: 'In the Frontend',
                        onClick: () => {
                          alert(
                            `Learn Frontend, ${data?.lastName} ${data?.firstName}`,
                          );
                        },
                      },
                    ],
                  },
                },
              ],
            };
          }}
        />
      </DataSource>
    </>
  );
}
```

## Context Menus for the Table Body

You might want to show a context menu for the table body, when the user right-clicks outside of any existing cell.

For this, you can use the [`getContextMenuItems`](https://infinite-table.com/docs/reference/infinite-table-props.md#getContextMenuItems) prop.

This function has almost the same signature as [`getCellContextMenuItems`](https://infinite-table.com/docs/reference/infinite-table-props.md#getCellContextMenuItems), with the following differences in the object passed as first parameter:

- all cell-related properties (`column`, `data`, `value`, etc) can be `undefined`
- it contains an `event` property with the original event object for the right-click event

**Example: Context menu for outside cells**

Right-click outside cells in the table to see a context menu for the table body.

```ts
import {
  InfiniteTable,
  DataSource,
  InfiniteTablePropColumns,
} from '@infinite-table/infinite-react';

import * as React from 'react';

type Developer = {
  id: number;
  firstName: string;
  lastName: string;
  country: string;
  city: string;
  currency: string;
  preferredLanguage: string;
  stack: string;
  canDesign: 'yes' | 'no';
  hobby: string;
  salary: number;
  age: number;
};

const dataSource = () => {
  return fetch(process.env.NEXT_PUBLIC_BASE_URL + '/developers100')
    .then((r) => r.json())
    .then((data: Developer[]) => data);
};

const columns: InfiniteTablePropColumns<Developer> = {
  firstName: {
    field: 'firstName',
    header: 'Name',
    defaultWidth: 120,
  },
  age: {
    field: 'age',
    header: 'Age',
    defaultWidth: 100,
  },
  preferredLanguage: {
    header: 'Language',
    defaultWidth: 120,
    field: 'preferredLanguage',
  },
};

export default function App() {
  return (
    <>
      <DataSource<Developer> primaryKey="id" data={dataSource}>
        <InfiniteTable<Developer>
          debugId="table-context-menu-example"
          columns={columns}
          getContextMenuItems={({ data, column }) => {
            if (!data)
              return [
                {
                  key: 'add',
                  label: 'Add Item',
                  onClick: () => {
                    alert('Add Item');
                  },
                },
              ];

            return [
              {
                key: 'hello',
                label: `Hello, ${data?.lastName} ${data?.firstName}`,
                onClick: () => {
                  alert(`Hello, ${data?.lastName} ${data?.firstName}`);
                },
              },
              {
                key: 'col',
                label: `Current clicked column: ${column?.header}`,
              },
              {
                key: 'learn',
                label: `Learn`,
                menu: {
                  items: [
                    {
                      key: 'backend',
                      label: 'Backend',
                      onClick: () => {
                        alert(
                          `Learn Backend, ${data?.lastName} ${data?.firstName}`,
                        );
                      },
                    },
                    {
                      key: 'frontend',
                      label: 'Frontend',
                      onClick: () => {
                        alert(
                          `Learn Frontend, ${data?.lastName} ${data?.firstName}`,
                        );
                      },
                    },
                  ],
                },
              },
            ];
          }}
        />
      </DataSource>
    </>
  );
}
```

## Hiding the Context Menu

To hide the context menu when you click a menu item, you can use the `hideMenuOnAction` property on the menu item.

Alternatively, you can use the object passed in as a parameter to the `item.onAction` callback function to hide the menu:

```tsx {12}
const getCellContextMenuItems = () => {
  return {
    items: [
      {
        label: 'Hello',
        key: 'hi',
        onAction: ({ key, hideMenu }) => {
          // do something
          console.log('Hello');

          // hide the menu
          hideMenu();
        },
      },
    ],
  };
};
```

The third option is to use the [`hideContextMenu`](https://infinite-table.com/docs/reference/api/index.md#hideContextMenu) function in the [API](https://infinite-table.com/docs/reference/api/index.md).
