Skip to content

Quickly access contextual actions such as removing rows, inserting columns or copying data, by opening the context menu.

Context menu with default options

Enable the context menu with the default configuration:

contextMenu: true,

To see the context menu, right-click on a cell:

JavaScript
import Handsontable from 'handsontable/base';
import { registerAllModules } from 'handsontable/registry';
// Register all Handsontable's modules.
registerAllModules();
const container = document.querySelector('#example1');
new Handsontable(container, {
data: [
['', 'Tesla', 'Nissan', 'Toyota', 'Honda', 'Mazda', 'Ford'],
['2017', 10, 11, 12, 13, 15, 16],
['2018', 10, 11, 12, 13, 15, 16],
['2019', 10, 11, 12, 13, 15, 16],
['2020', 10, 11, 12, 13, 15, 16],
['2021', 10, 11, 12, 13, 15, 16],
],
rowHeaders: true,
colHeaders: true,
contextMenu: true,
height: 'auto',
autoWrapRow: true,
autoWrapCol: true,
licenseKey: 'non-commercial-and-evaluation',
});
TypeScript
import Handsontable from 'handsontable/base';
import { registerAllModules } from 'handsontable/registry';
// Register all Handsontable's modules.
registerAllModules();
const container = document.querySelector('#example1')!;
new Handsontable(container, {
data: [
['', 'Tesla', 'Nissan', 'Toyota', 'Honda', 'Mazda', 'Ford'],
['2017', 10, 11, 12, 13, 15, 16],
['2018', 10, 11, 12, 13, 15, 16],
['2019', 10, 11, 12, 13, 15, 16],
['2020', 10, 11, 12, 13, 15, 16],
['2021', 10, 11, 12, 13, 15, 16],
],
rowHeaders: true,
colHeaders: true,
contextMenu: true,
height: 'auto',
autoWrapRow: true,
autoWrapCol: true,
licenseKey: 'non-commercial-and-evaluation',
});

Context menu with selected options

You can define the items in the menu by passing the contextMenu option as an array of keys:

KeyAction and required plugins
row_aboveInsert a row above
row_belowInsert a row below
col_leftInsert a column to the left
col_rightInsert a column to the right
---------Add a separator to the items in the menu
remove_rowRemove the selected row
remove_colRemove the selected column
clear_columnDelete the data of the selected columns
undoUndo the last action. Requires: UndoRedo
redoRedo the last action. Requires: UndoRedo
make_read_onlyMake the selected cells read-only
alignmentAlign the text in the cell
cutCut the contents of the selected cells to the system clipboard. Requires: CopyPaste
copyCopy the contents of the selected cells to the system clipboard. Requires: CopyPaste
copy_with_column_headersCopy the contents of the selected cells and their nearest column headers. Requires: CopyPaste with copyColumnHeaders set to true
copy_with_column_group_headersCopy the contents of the selected cells and all their related column headers. Requires: NestedHeaders and CopyPaste with copyColumnGroupHeaders set to true
copy_column_headers_onlyCopy the contents of column headers that are nearest to the selected cells. Requires: CopyPaste with copyColumnHeadersOnly set to true
freeze_columnFreeze the selected column. Requires: ManualColumnFreeze
unfreeze_columnUnfreeze the selected column. Requires: ManualColumnFreeze
bordersAdd borders around the selected cells. Requires: CustomBorders
commentsAddEditAdd or edit a comment. Requires: Comments
commentsRemoveRemove the comment. Requires: Comments
commentsReadOnlyMake the comment read-only. Requires: Comments
mergeCellsMerge or unmerge the selected cells. Requires: MergeCells
add_childInsert a child row. Requires: NestedRows
detach_from_parentDetach the selected row from its parent row. Requires: NestedRows
hidden_columns_hideHide the selected columns. Requires: HiddenColumns
hidden_columns_showShow the hidden columns. Requires: HiddenColumns
hidden_rows_hideHide the selected rows. Requires: HiddenRows
hidden_rows_showShow hidden rows. Requires: HiddenRows
filter_by_conditionAdd the first filter condition. Requires: Filters
filter_by_condition2Add the second filter condition. Requires: Filters
filter_operatorsSelect a filter parameter. Requires: Filters
filter_by_valueAdd a filter value. Requires: Filters
filter_action_barApply the configured filter. Requires: Filters
export_fileOpen the Export submenu with “To CSV” and “To Excel” items. Requires: ExportFile. The Excel item is hidden when no XLSX engine is configured.

To see the context menu, right-click on a cell:

JavaScript
import Handsontable from 'handsontable/base';
import { registerAllModules } from 'handsontable/registry';
// Register all Handsontable's modules.
registerAllModules();
const container = document.querySelector('#example2');
new Handsontable(container, {
data: [
['', 'Tesla', 'Nissan', 'Toyota', 'Honda', 'Mazda', 'Ford'],
['2017', 10, 11, 12, 13, 15, 16],
['2018', 10, 11, 12, 13, 15, 16],
['2019', 10, 11, 12, 13, 15, 16],
['2020', 10, 11, 12, 13, 15, 16],
['2021', 10, 11, 12, 13, 15, 16],
],
rowHeaders: true,
colHeaders: true,
contextMenu: ['row_above', 'row_below', 'remove_row', 'clear_column'],
height: 'auto',
autoWrapRow: true,
autoWrapCol: true,
licenseKey: 'non-commercial-and-evaluation',
});
TypeScript
import Handsontable from 'handsontable/base';
import { registerAllModules } from 'handsontable/registry';
// Register all Handsontable's modules.
registerAllModules();
const container = document.querySelector('#example2')!;
new Handsontable(container, {
data: [
['', 'Tesla', 'Nissan', 'Toyota', 'Honda', 'Mazda', 'Ford'],
['2017', 10, 11, 12, 13, 15, 16],
['2018', 10, 11, 12, 13, 15, 16],
['2019', 10, 11, 12, 13, 15, 16],
['2020', 10, 11, 12, 13, 15, 16],
['2021', 10, 11, 12, 13, 15, 16],
],
rowHeaders: true,
colHeaders: true,
contextMenu: ['row_above', 'row_below', 'remove_row', 'clear_column'],
height: 'auto',
autoWrapRow: true,
autoWrapCol: true,
licenseKey: 'non-commercial-and-evaluation',
});

Context menu with a fully custom configuration

To fully customize the context menu, set contextMenu to an object with an items property. Each key in items identifies one menu entry. The value can be:

  • A predefined item key string such as 'row_above' — includes the built-in item unchanged.
  • The string '---------' — inserts a horizontal separator line.
  • A configuration object — defines a custom item or overrides a predefined one.

This means you can freely mix built-in items with custom ones in the same menu:

contextMenu: {
// Optional: a shared callback fired on every item click.
callback(key, selection, clickEvent) {
console.log(key, selection, clickEvent);
},
items: {
row_above: {}, // predefined item, unchanged
sp1: '---------', // separator
row_below: {
name: 'Click to add row below', // override a predefined item's label
},
myOption: { // fully custom item
name: 'My custom action',
callback() { /* ... */ },
},
},
},

The top-level object also accepts a uiContainer property (an HTMLElement) to control which DOM element the context menu is appended to.

Each configuration object in items can have these properties:

OptionDescription
keyThe unique identifier for the item. For top-level items, this is the key of the items object (for example 'row_above' or 'myOption'). For submenu items, it must follow the parent_key:child_key format (for example 'colors:red').
nameThe label shown in the menu. Can be a string or a function returning a string. Supports HTML. When a function, this refers to the Handsontable instance.
disabledWhether the item is grayed out and non-clickable. Can be a boolean or a function returning a boolean. When a function, this refers to the Handsontable instance.
hiddenWhether the item is hidden from the menu entirely. Can be a boolean or a function returning a boolean. When a function, this refers to the Handsontable instance.
callbackA function called when the item is clicked. Receives key, selection, and clickEvent as arguments.
submenuDefines a nested submenu. Takes an object with an items array. Each submenu item’s key must follow the parent_key:child_key format.
rendererA custom function for rendering the item’s HTML. Must return an HTMLElement.
disableSelectionWhen true, hovering over the item does not highlight it.
isCommandWhen false, clicking the item does not execute a command or close the menu.

The following example shows how to:

  • Add a shared callback for all options
  • Dynamically disable an option
  • Override the label of a predefined option
  • Add a fully custom option with its own callback
  • Add a custom option with a submenu
  • Use a custom renderer

To see the context menu, right-click on a cell:

JavaScript
import Handsontable from 'handsontable/base';
import { registerAllModules } from 'handsontable/registry';
// Register all Handsontable's modules.
registerAllModules();
const contextMenuSettings = {
callback(key, selection, clickEvent) {
// Common callback for all options
console.log(key, selection, clickEvent);
},
items: {
row_above: {
disabled() {
// `disabled` can be a boolean or a function
// Disable option when first row was clicked
return this.getSelectedLast()?.[0] === 0; // `this` === hot
},
},
// A separator line can also be added like this:
// 'sp1': { name: '---------' }
// and the key has to be unique
sp1: '---------',
row_below: {
name: 'Click to add row below', // Set custom text for predefined option
},
about: {
// Own custom option
name() {
// `name` can be a string or a function
return '<b>Custom option</b>'; // Name can contain HTML
},
hidden() {
// `hidden` can be a boolean or a function
// Hide the option when the first column was clicked
return this.getSelectedLast()?.[1] == 0; // `this` === hot
},
callback() {
// Callback for specific option
setTimeout(() => {
alert('Hello world!'); // Fire alert after menu close (with timeout)
}, 0);
},
},
colors: {
// Own custom option
name: 'Colors...',
submenu: {
// Custom option with submenu of items
items: [
{
// Key must be in the form 'parent_key:child_key'
key: 'colors:red',
name: 'Red',
callback() {
setTimeout(() => {
alert('You clicked red!');
}, 0);
},
},
{ key: 'colors:green', name: 'Green' },
{ key: 'colors:blue', name: 'Blue' },
],
},
},
credits: {
// Own custom property
// Custom rendered element in the context menu
renderer() {
const elem = document.createElement('marquee');
elem.style.cssText = 'background: lightgray; color: #222222;';
elem.textContent = 'Brought to you by...';
return elem;
},
disableSelection: true,
isCommand: false, // Prevent clicks from executing command and closing the menu
},
},
};
const container = document.querySelector('#example3');
new Handsontable(container, {
data: [
['', 'Tesla', 'Nissan', 'Toyota', 'Honda', 'Mazda', 'Ford'],
['2017', 10, 11, 12, 13, 15, 16],
['2018', 10, 11, 12, 13, 15, 16],
['2019', 10, 11, 12, 13, 15, 16],
['2020', 10, 11, 12, 13, 15, 16],
['2021', 10, 11, 12, 13, 15, 16],
],
rowHeaders: true,
colHeaders: true,
licenseKey: 'non-commercial-and-evaluation',
height: 'auto',
contextMenu: contextMenuSettings,
autoWrapRow: true,
autoWrapCol: true,
});
TypeScript
import Handsontable from 'handsontable/base';
import { registerAllModules } from 'handsontable/registry';
import { DetailedSettings, MenuItemConfig } from 'handsontable/plugins/contextMenu';
// Register all Handsontable's modules.
registerAllModules();
const contextMenuSettings: DetailedSettings = {
callback(key, selection, clickEvent) {
// Common callback for all options
console.log(key, selection, clickEvent);
},
items: {
row_above: {
disabled() {
// `disabled` can be a boolean or a function
// Disable option when first row was clicked
return this.getSelectedLast()?.[0] === 0; // `this` === hot
},
},
// A separator line can also be added like this:
// 'sp1': { name: '---------' }
// and the key has to be unique
sp1: '---------' as MenuItemConfig,
row_below: {
name: 'Click to add row below', // Set custom text for predefined option
},
about: {
// Own custom option
name() {
// `name` can be a string or a function
return '<b>Custom option</b>'; // Name can contain HTML
},
hidden() {
// `hidden` can be a boolean or a function
// Hide the option when the first column was clicked
return this.getSelectedLast()?.[1] == 0; // `this` === hot
},
callback() {
// Callback for specific option
setTimeout(() => {
alert('Hello world!'); // Fire alert after menu close (with timeout)
}, 0);
},
},
colors: {
// Own custom option
name: 'Colors...',
submenu: {
// Custom option with submenu of items
items: [
{
// Key must be in the form 'parent_key:child_key'
key: 'colors:red',
name: 'Red',
callback() {
setTimeout(() => {
alert('You clicked red!');
}, 0);
},
},
{ key: 'colors:green', name: 'Green' },
{ key: 'colors:blue', name: 'Blue' },
],
},
},
credits: {
// Own custom property
// Custom rendered element in the context menu
renderer() {
const elem = document.createElement('marquee');
elem.style.cssText = 'background: lightgray; color: #222222;';
elem.textContent = 'Brought to you by...';
return elem;
},
disableSelection: true, // Prevent mouseoever from highlighting the item for selection
isCommand: false, // Prevent clicks from executing command and closing the menu
},
},
};
const container = document.querySelector('#example3')!;
new Handsontable(container, {
data: [
['', 'Tesla', 'Nissan', 'Toyota', 'Honda', 'Mazda', 'Ford'],
['2017', 10, 11, 12, 13, 15, 16],
['2018', 10, 11, 12, 13, 15, 16],
['2019', 10, 11, 12, 13, 15, 16],
['2020', 10, 11, 12, 13, 15, 16],
['2021', 10, 11, 12, 13, 15, 16],
],
rowHeaders: true,
colHeaders: true,
licenseKey: 'non-commercial-and-evaluation',
height: 'auto',
contextMenu: contextMenuSettings,
autoWrapRow: true,
autoWrapCol: true,
});
WindowsmacOSActionExcelSheets
Ctrl+Shift+\ or Shift+F10Cmd+Shift+\ or Shift+F10Open the context menu
Arrow keysArrow keysMove one available menu item up, down, left, or right
Page UpPage UpMove to the first visible item of the context menu or submenu
Page DownPage DownMove to the last visible item of the context menu or submenu
EscapeEscapeClose the context menu or submenu
EnterEnterRun the action of the selected menu item

Related guides

Related blog articles

Configuration options

Hooks

Plugins