Skip to content

Dialog

Display modal dialogs, alerts, loading indicators, and notifications to enhance user interaction and provide feedback in your data grid application.

Overview

The Dialog plugin provides a modal dialog system for Handsontable that allows you to display custom content in modal dialogs that overlay the table. This is useful for showing notifications, error messages, loading indicators, or any other interactive content that requires user attention.

The dialog system is designed to be flexible and customizable, supporting various content types including plain text, HTML, and DOM elements. It also provides options for styling, animations, and user interaction controls.

The plugin works on the main grid only. A grid nested in a cell, that is a cell of the handsontable, autocomplete, or dropdown cell type, has no overlay layer to render a dialog into, so the dialog option has no effect there.

Basic configuration

To enable the Dialog plugin, set the dialog option to true or provide a configuration object.

JavaScript
import Handsontable from 'handsontable/base';
import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modules
registerAllModules();
const data = [
{ model: 'Trail Helmet', price: 1298.14, sellDate: '2025-08-31', sellTime: '14:12', inStock: true },
{ model: 'Windbreaker Jacket', price: 178.9, sellDate: '2025-05-10', sellTime: '22:26', inStock: false },
{ model: 'Cycling Cap', price: 288.1, sellDate: '2025-09-15', sellTime: '09:37', inStock: true },
{ model: 'HL Mountain Frame', price: 94.49, sellDate: '2025-01-17', sellTime: '14:19', inStock: false },
{ model: 'Racing Socks', price: 430.38, sellDate: '2025-05-10', sellTime: '13:42', inStock: true },
{ model: 'Racing Socks', price: 138.85, sellDate: '2025-09-20', sellTime: '14:48', inStock: true },
{ model: 'HL Mountain Frame', price: 1909.63, sellDate: '2025-09-05', sellTime: '09:35', inStock: false },
{ model: 'Carbon Handlebar', price: 1080.7, sellDate: '2025-10-24', sellTime: '22:58', inStock: false },
{ model: 'Aero Bottle', price: 1571.13, sellDate: '2025-05-24', sellTime: '00:24', inStock: true },
{ model: 'Windbreaker Jacket', price: 919.09, sellDate: '2025-07-16', sellTime: '19:11', inStock: true },
{ model: 'HL Road Tire', price: 886.22, sellDate: '2025-09-09', sellTime: '00:42', inStock: false },
{ model: 'Speed Gloves', price: 635.13, sellDate: '2025-11-17', sellTime: '12:45', inStock: true },
{ model: 'Trail Helmet', price: 1440.64, sellDate: '2025-01-03', sellTime: '20:16', inStock: false },
{ model: 'Aero Bottle', price: 944.63, sellDate: '2025-11-15', sellTime: '16:14', inStock: false },
{ model: 'Windbreaker Jacket', price: 1161.43, sellDate: '2025-06-24', sellTime: '13:19', inStock: false },
{ model: 'LED Bike Light', price: 1012.5, sellDate: '2025-05-01', sellTime: '17:30', inStock: false },
{ model: 'Windbreaker Jacket', price: 635.37, sellDate: '2025-05-14', sellTime: '09:05', inStock: true },
{ model: 'Road Tire Tube', price: 1421.27, sellDate: '2025-01-31', sellTime: '13:33', inStock: true },
{ model: 'Action Camera', price: 1019.05, sellDate: '2025-12-07', sellTime: '01:26', inStock: false },
{ model: 'Carbon Handlebar', price: 603.96, sellDate: '2025-09-13', sellTime: '04:10', inStock: false },
];
const container = document.getElementById('example1');
const hot = new Handsontable(container, {
data,
colHeaders: true,
rowHeaders: true,
columns: [
{
title: 'Model',
type: 'text',
data: 'model',
width: 150,
headerClassName: 'htLeft',
},
{
title: 'Price',
type: 'numeric',
data: 'price',
width: 80,
locale: 'en-US',
numericFormat: {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
},
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'Date',
type: 'intl-date',
data: 'sellDate',
width: 131,
locale: 'en-US',
dateFormat: { month: 'short', day: 'numeric', year: 'numeric' },
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'Time',
type: 'intl-time',
data: 'sellTime',
width: 90,
locale: 'en-US',
timeFormat: { hour: '2-digit', minute: '2-digit', hour12: true },
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'In stock',
type: 'checkbox',
data: 'inStock',
className: 'htCenter',
headerClassName: 'htCenter',
},
],
width: '100%',
height: 300,
stretchH: 'all',
dialog: true,
licenseKey: 'non-commercial-and-evaluation',
});
// Show dialog after initialization
const dialogPlugin = hot.getPlugin('dialog');
dialogPlugin.show({
content: 'This is a basic dialog with default configuration.',
closable: true,
});
TypeScript
import Handsontable from 'handsontable/base';
import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modules
registerAllModules();
const data = [
{ model: 'Trail Helmet', price: 1298.14, sellDate: '2025-08-31', sellTime: '14:12', inStock: true },
{ model: 'Windbreaker Jacket', price: 178.9, sellDate: '2025-05-10', sellTime: '22:26', inStock: false },
{ model: 'Cycling Cap', price: 288.1, sellDate: '2025-09-15', sellTime: '09:37', inStock: true },
{ model: 'HL Mountain Frame', price: 94.49, sellDate: '2025-01-17', sellTime: '14:19', inStock: false },
{ model: 'Racing Socks', price: 430.38, sellDate: '2025-05-10', sellTime: '13:42', inStock: true },
{ model: 'Racing Socks', price: 138.85, sellDate: '2025-09-20', sellTime: '14:48', inStock: true },
{ model: 'HL Mountain Frame', price: 1909.63, sellDate: '2025-09-05', sellTime: '09:35', inStock: false },
{ model: 'Carbon Handlebar', price: 1080.7, sellDate: '2025-10-24', sellTime: '22:58', inStock: false },
{ model: 'Aero Bottle', price: 1571.13, sellDate: '2025-05-24', sellTime: '00:24', inStock: true },
{ model: 'Windbreaker Jacket', price: 919.09, sellDate: '2025-07-16', sellTime: '19:11', inStock: true },
{ model: 'HL Road Tire', price: 886.22, sellDate: '2025-09-09', sellTime: '00:42', inStock: false },
{ model: 'Speed Gloves', price: 635.13, sellDate: '2025-11-17', sellTime: '12:45', inStock: true },
{ model: 'Trail Helmet', price: 1440.64, sellDate: '2025-01-03', sellTime: '20:16', inStock: false },
{ model: 'Aero Bottle', price: 944.63, sellDate: '2025-11-15', sellTime: '16:14', inStock: false },
{ model: 'Windbreaker Jacket', price: 1161.43, sellDate: '2025-06-24', sellTime: '13:19', inStock: false },
{ model: 'LED Bike Light', price: 1012.5, sellDate: '2025-05-01', sellTime: '17:30', inStock: false },
{ model: 'Windbreaker Jacket', price: 635.37, sellDate: '2025-05-14', sellTime: '09:05', inStock: true },
{ model: 'Road Tire Tube', price: 1421.27, sellDate: '2025-01-31', sellTime: '13:33', inStock: true },
{ model: 'Action Camera', price: 1019.05, sellDate: '2025-12-07', sellTime: '01:26', inStock: false },
{ model: 'Carbon Handlebar', price: 603.96, sellDate: '2025-09-13', sellTime: '04:10', inStock: false },
];
const container = document.getElementById('example1')!;
const hot = new Handsontable(container, {
data,
colHeaders: true,
rowHeaders: true,
columns: [
{
title: 'Model',
type: 'text',
data: 'model',
width: 150,
headerClassName: 'htLeft',
},
{
title: 'Price',
type: 'numeric',
data: 'price',
width: 80,
locale: 'en-US',
numericFormat: {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
},
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'Date',
type: 'intl-date',
data: 'sellDate',
width: 131,
locale: 'en-US',
dateFormat: { month: 'short', day: 'numeric', year: 'numeric' },
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'Time',
type: 'intl-time',
data: 'sellTime',
width: 90,
locale: 'en-US',
timeFormat: { hour: '2-digit', minute: '2-digit', hour12: true },
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'In stock',
type: 'checkbox',
data: 'inStock',
className: 'htCenter',
headerClassName: 'htCenter',
},
],
width: '100%',
height: 300,
stretchH: 'all',
dialog: true,
licenseKey: 'non-commercial-and-evaluation',
});
// Show dialog after initialization
const dialogPlugin = hot.getPlugin('dialog');
dialogPlugin.show({
content: 'This is a basic dialog with default configuration.',
closable: true,
});

Content types

The dialog supports multiple content types including plain text, HTML strings, and DOM elements.

Plain text content

JavaScript
import Handsontable from 'handsontable/base';
import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modules
registerAllModules();
const data = [
{ model: 'Trail Helmet', price: 1298.14, sellDate: '2025-08-31', sellTime: '14:12', inStock: true },
{ model: 'Windbreaker Jacket', price: 178.9, sellDate: '2025-05-10', sellTime: '22:26', inStock: false },
{ model: 'Cycling Cap', price: 288.1, sellDate: '2025-09-15', sellTime: '09:37', inStock: true },
{ model: 'HL Mountain Frame', price: 94.49, sellDate: '2025-01-17', sellTime: '14:19', inStock: false },
{ model: 'Racing Socks', price: 430.38, sellDate: '2025-05-10', sellTime: '13:42', inStock: true },
{ model: 'Racing Socks', price: 138.85, sellDate: '2025-09-20', sellTime: '14:48', inStock: true },
{ model: 'HL Mountain Frame', price: 1909.63, sellDate: '2025-09-05', sellTime: '09:35', inStock: false },
{ model: 'Carbon Handlebar', price: 1080.7, sellDate: '2025-10-24', sellTime: '22:58', inStock: false },
{ model: 'Aero Bottle', price: 1571.13, sellDate: '2025-05-24', sellTime: '00:24', inStock: true },
{ model: 'Windbreaker Jacket', price: 919.09, sellDate: '2025-07-16', sellTime: '19:11', inStock: true },
{ model: 'HL Road Tire', price: 886.22, sellDate: '2025-09-09', sellTime: '00:42', inStock: false },
{ model: 'Speed Gloves', price: 635.13, sellDate: '2025-11-17', sellTime: '12:45', inStock: true },
{ model: 'Trail Helmet', price: 1440.64, sellDate: '2025-01-03', sellTime: '20:16', inStock: false },
{ model: 'Aero Bottle', price: 944.63, sellDate: '2025-11-15', sellTime: '16:14', inStock: false },
{ model: 'Windbreaker Jacket', price: 1161.43, sellDate: '2025-06-24', sellTime: '13:19', inStock: false },
{ model: 'LED Bike Light', price: 1012.5, sellDate: '2025-05-01', sellTime: '17:30', inStock: false },
{ model: 'Windbreaker Jacket', price: 635.37, sellDate: '2025-05-14', sellTime: '09:05', inStock: true },
{ model: 'Road Tire Tube', price: 1421.27, sellDate: '2025-01-31', sellTime: '13:33', inStock: true },
{ model: 'Action Camera', price: 1019.05, sellDate: '2025-12-07', sellTime: '01:26', inStock: false },
{ model: 'Carbon Handlebar', price: 603.96, sellDate: '2025-09-13', sellTime: '04:10', inStock: false },
];
const container = document.getElementById('example2');
const hot = new Handsontable(container, {
data,
colHeaders: true,
rowHeaders: true,
columns: [
{
title: 'Model',
type: 'text',
data: 'model',
width: 150,
headerClassName: 'htLeft',
},
{
title: 'Price',
type: 'numeric',
data: 'price',
width: 80,
locale: 'en-US',
numericFormat: {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
},
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'Date',
type: 'intl-date',
data: 'sellDate',
width: 131,
locale: 'en-US',
dateFormat: { month: 'short', day: 'numeric', year: 'numeric' },
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'Time',
type: 'intl-time',
data: 'sellTime',
width: 90,
locale: 'en-US',
timeFormat: { hour: '2-digit', minute: '2-digit', hour12: true },
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'In stock',
type: 'checkbox',
data: 'inStock',
className: 'htCenter',
headerClassName: 'htCenter',
},
],
width: '100%',
height: 300,
stretchH: 'all',
dialog: {
content: 'This is a simple text message displayed in the dialog.',
closable: true,
},
licenseKey: 'non-commercial-and-evaluation',
});
// Show dialog after initialization
const dialogPlugin = hot.getPlugin('dialog');
dialogPlugin.show();
TypeScript
import Handsontable from 'handsontable/base';
import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modules
registerAllModules();
const data = [
{ model: 'Trail Helmet', price: 1298.14, sellDate: '2025-08-31', sellTime: '14:12', inStock: true },
{ model: 'Windbreaker Jacket', price: 178.9, sellDate: '2025-05-10', sellTime: '22:26', inStock: false },
{ model: 'Cycling Cap', price: 288.1, sellDate: '2025-09-15', sellTime: '09:37', inStock: true },
{ model: 'HL Mountain Frame', price: 94.49, sellDate: '2025-01-17', sellTime: '14:19', inStock: false },
{ model: 'Racing Socks', price: 430.38, sellDate: '2025-05-10', sellTime: '13:42', inStock: true },
{ model: 'Racing Socks', price: 138.85, sellDate: '2025-09-20', sellTime: '14:48', inStock: true },
{ model: 'HL Mountain Frame', price: 1909.63, sellDate: '2025-09-05', sellTime: '09:35', inStock: false },
{ model: 'Carbon Handlebar', price: 1080.7, sellDate: '2025-10-24', sellTime: '22:58', inStock: false },
{ model: 'Aero Bottle', price: 1571.13, sellDate: '2025-05-24', sellTime: '00:24', inStock: true },
{ model: 'Windbreaker Jacket', price: 919.09, sellDate: '2025-07-16', sellTime: '19:11', inStock: true },
{ model: 'HL Road Tire', price: 886.22, sellDate: '2025-09-09', sellTime: '00:42', inStock: false },
{ model: 'Speed Gloves', price: 635.13, sellDate: '2025-11-17', sellTime: '12:45', inStock: true },
{ model: 'Trail Helmet', price: 1440.64, sellDate: '2025-01-03', sellTime: '20:16', inStock: false },
{ model: 'Aero Bottle', price: 944.63, sellDate: '2025-11-15', sellTime: '16:14', inStock: false },
{ model: 'Windbreaker Jacket', price: 1161.43, sellDate: '2025-06-24', sellTime: '13:19', inStock: false },
{ model: 'LED Bike Light', price: 1012.5, sellDate: '2025-05-01', sellTime: '17:30', inStock: false },
{ model: 'Windbreaker Jacket', price: 635.37, sellDate: '2025-05-14', sellTime: '09:05', inStock: true },
{ model: 'Road Tire Tube', price: 1421.27, sellDate: '2025-01-31', sellTime: '13:33', inStock: true },
{ model: 'Action Camera', price: 1019.05, sellDate: '2025-12-07', sellTime: '01:26', inStock: false },
{ model: 'Carbon Handlebar', price: 603.96, sellDate: '2025-09-13', sellTime: '04:10', inStock: false },
];
const container = document.getElementById('example2')!;
const hot = new Handsontable(container, {
data,
colHeaders: true,
rowHeaders: true,
columns: [
{
title: 'Model',
type: 'text',
data: 'model',
width: 150,
headerClassName: 'htLeft',
},
{
title: 'Price',
type: 'numeric',
data: 'price',
width: 80,
locale: 'en-US',
numericFormat: {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
},
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'Date',
type: 'intl-date',
data: 'sellDate',
width: 131,
locale: 'en-US',
dateFormat: { month: 'short', day: 'numeric', year: 'numeric' },
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'Time',
type: 'intl-time',
data: 'sellTime',
width: 90,
locale: 'en-US',
timeFormat: { hour: '2-digit', minute: '2-digit', hour12: true },
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'In stock',
type: 'checkbox',
data: 'inStock',
className: 'htCenter',
headerClassName: 'htCenter',
},
],
width: '100%',
height: 300,
stretchH: 'all',
dialog: {
content: 'This is a simple text message displayed in the dialog.',
closable: true,
},
licenseKey: 'non-commercial-and-evaluation',
});
// Show dialog after initialization
const dialogPlugin = hot.getPlugin('dialog');
dialogPlugin.show();

HTML content

JavaScript
import Handsontable from 'handsontable/base';
import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modules
registerAllModules();
const ALLOWED_TAGS = ['H2', 'P', 'STRONG', 'BUTTON', 'BR', 'TABLE', 'THEAD', 'TBODY', 'TR', 'TD', 'TH'];
const ALLOWED_ATTRIBUTES = ['id', 'class', 'type', 'colspan', 'rowspan'];
const DROPPED_TAGS = ['SCRIPT', 'STYLE', 'TEXTAREA', 'TITLE'];
// Handsontable has no built-in sanitizer since v18.0, and `sanitizer` is grid-level:
// it also filters pasted HTML, so the table tags have to survive -- otherwise pasting
// a range degrades to plain text. In production, use a vetted library such as DOMPurify.
// See https://handsontable.com/docs/security/
const sanitizeDialogContent = (html) => {
const template = document.createElement('template');
template.innerHTML = html;
template.content.querySelectorAll('*').forEach((element) => {
if (DROPPED_TAGS.includes(element.tagName)) {
// Unwrapping these would promote their source text into the output
element.remove();
} else if (ALLOWED_TAGS.includes(element.tagName)) {
Array.from(element.attributes).forEach((attribute) => {
if (!ALLOWED_ATTRIBUTES.includes(attribute.name)) {
element.removeAttribute(attribute.name);
}
});
} else {
// Unwrap a disallowed element, keeping its text content
element.replaceWith(...Array.from(element.childNodes));
}
});
return template.innerHTML;
};
const data = [
{ model: 'Trail Helmet', price: 1298.14, sellDate: '2025-08-31', sellTime: '14:12', inStock: true },
{ model: 'Windbreaker Jacket', price: 178.9, sellDate: '2025-05-10', sellTime: '22:26', inStock: false },
{ model: 'Cycling Cap', price: 288.1, sellDate: '2025-09-15', sellTime: '09:37', inStock: true },
{ model: 'HL Mountain Frame', price: 94.49, sellDate: '2025-01-17', sellTime: '14:19', inStock: false },
{ model: 'Racing Socks', price: 430.38, sellDate: '2025-05-10', sellTime: '13:42', inStock: true },
{ model: 'Racing Socks', price: 138.85, sellDate: '2025-09-20', sellTime: '14:48', inStock: true },
{ model: 'HL Mountain Frame', price: 1909.63, sellDate: '2025-09-05', sellTime: '09:35', inStock: false },
{ model: 'Carbon Handlebar', price: 1080.7, sellDate: '2025-10-24', sellTime: '22:58', inStock: false },
{ model: 'Aero Bottle', price: 1571.13, sellDate: '2025-05-24', sellTime: '00:24', inStock: true },
{ model: 'Windbreaker Jacket', price: 919.09, sellDate: '2025-07-16', sellTime: '19:11', inStock: true },
{ model: 'HL Road Tire', price: 886.22, sellDate: '2025-09-09', sellTime: '00:42', inStock: false },
{ model: 'Speed Gloves', price: 635.13, sellDate: '2025-11-17', sellTime: '12:45', inStock: true },
{ model: 'Trail Helmet', price: 1440.64, sellDate: '2025-01-03', sellTime: '20:16', inStock: false },
{ model: 'Aero Bottle', price: 944.63, sellDate: '2025-11-15', sellTime: '16:14', inStock: false },
{ model: 'Windbreaker Jacket', price: 1161.43, sellDate: '2025-06-24', sellTime: '13:19', inStock: false },
{ model: 'LED Bike Light', price: 1012.5, sellDate: '2025-05-01', sellTime: '17:30', inStock: false },
{ model: 'Windbreaker Jacket', price: 635.37, sellDate: '2025-05-14', sellTime: '09:05', inStock: true },
{ model: 'Road Tire Tube', price: 1421.27, sellDate: '2025-01-31', sellTime: '13:33', inStock: true },
{ model: 'Action Camera', price: 1019.05, sellDate: '2025-12-07', sellTime: '01:26', inStock: false },
{ model: 'Carbon Handlebar', price: 603.96, sellDate: '2025-09-13', sellTime: '04:10', inStock: false },
];
const container = document.getElementById('example3');
const hot = new Handsontable(container, {
data,
colHeaders: true,
rowHeaders: true,
columns: [
{
title: 'Model',
type: 'text',
data: 'model',
width: 150,
headerClassName: 'htLeft',
},
{
title: 'Price',
type: 'numeric',
data: 'price',
width: 80,
locale: 'en-US',
numericFormat: {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
},
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'Date',
type: 'intl-date',
data: 'sellDate',
width: 131,
locale: 'en-US',
dateFormat: { month: 'short', day: 'numeric', year: 'numeric' },
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'Time',
type: 'intl-time',
data: 'sellTime',
width: 90,
locale: 'en-US',
timeFormat: { hour: '2-digit', minute: '2-digit', hour12: true },
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'In stock',
type: 'checkbox',
data: 'inStock',
className: 'htCenter',
headerClassName: 'htCenter',
},
],
width: '100%',
height: 300,
stretchH: 'all',
dialog: {
content:
'<p>This dialog contains <strong>HTML</strong> content with formatting.</p><button type="button" class="hot-doc-dialog-html-button" id="example3-button">Hide dialog</button>',
closable: true,
},
// Required for the dialog's HTML content to be rendered safely
sanitizer: sanitizeDialogContent,
licenseKey: 'non-commercial-and-evaluation',
});
// Show dialog after initialization
const dialogPlugin = hot.getPlugin('dialog');
dialogPlugin.show();
document.getElementById('example3-button').addEventListener('click', () => {
dialogPlugin.hide();
});
TypeScript
import Handsontable from 'handsontable/base';
import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modules
registerAllModules();
const ALLOWED_TAGS = ['H2', 'P', 'STRONG', 'BUTTON', 'BR', 'TABLE', 'THEAD', 'TBODY', 'TR', 'TD', 'TH'];
const ALLOWED_ATTRIBUTES = ['id', 'class', 'type', 'colspan', 'rowspan'];
const DROPPED_TAGS = ['SCRIPT', 'STYLE', 'TEXTAREA', 'TITLE'];
// Handsontable has no built-in sanitizer since v18.0, and `sanitizer` is grid-level:
// it also filters pasted HTML, so the table tags have to survive -- otherwise pasting
// a range degrades to plain text. In production, use a vetted library such as DOMPurify.
// See https://handsontable.com/docs/security/
const sanitizeDialogContent = (html: string): string => {
const template = document.createElement('template');
template.innerHTML = html;
template.content.querySelectorAll('*').forEach((element) => {
if (DROPPED_TAGS.includes(element.tagName)) {
// Unwrapping these would promote their source text into the output
element.remove();
} else if (ALLOWED_TAGS.includes(element.tagName)) {
Array.from(element.attributes).forEach((attribute) => {
if (!ALLOWED_ATTRIBUTES.includes(attribute.name)) {
element.removeAttribute(attribute.name);
}
});
} else {
// Unwrap a disallowed element, keeping its text content
element.replaceWith(...Array.from(element.childNodes));
}
});
return template.innerHTML;
};
const data = [
{ model: 'Trail Helmet', price: 1298.14, sellDate: '2025-08-31', sellTime: '14:12', inStock: true },
{ model: 'Windbreaker Jacket', price: 178.9, sellDate: '2025-05-10', sellTime: '22:26', inStock: false },
{ model: 'Cycling Cap', price: 288.1, sellDate: '2025-09-15', sellTime: '09:37', inStock: true },
{ model: 'HL Mountain Frame', price: 94.49, sellDate: '2025-01-17', sellTime: '14:19', inStock: false },
{ model: 'Racing Socks', price: 430.38, sellDate: '2025-05-10', sellTime: '13:42', inStock: true },
{ model: 'Racing Socks', price: 138.85, sellDate: '2025-09-20', sellTime: '14:48', inStock: true },
{ model: 'HL Mountain Frame', price: 1909.63, sellDate: '2025-09-05', sellTime: '09:35', inStock: false },
{ model: 'Carbon Handlebar', price: 1080.7, sellDate: '2025-10-24', sellTime: '22:58', inStock: false },
{ model: 'Aero Bottle', price: 1571.13, sellDate: '2025-05-24', sellTime: '00:24', inStock: true },
{ model: 'Windbreaker Jacket', price: 919.09, sellDate: '2025-07-16', sellTime: '19:11', inStock: true },
{ model: 'HL Road Tire', price: 886.22, sellDate: '2025-09-09', sellTime: '00:42', inStock: false },
{ model: 'Speed Gloves', price: 635.13, sellDate: '2025-11-17', sellTime: '12:45', inStock: true },
{ model: 'Trail Helmet', price: 1440.64, sellDate: '2025-01-03', sellTime: '20:16', inStock: false },
{ model: 'Aero Bottle', price: 944.63, sellDate: '2025-11-15', sellTime: '16:14', inStock: false },
{ model: 'Windbreaker Jacket', price: 1161.43, sellDate: '2025-06-24', sellTime: '13:19', inStock: false },
{ model: 'LED Bike Light', price: 1012.5, sellDate: '2025-05-01', sellTime: '17:30', inStock: false },
{ model: 'Windbreaker Jacket', price: 635.37, sellDate: '2025-05-14', sellTime: '09:05', inStock: true },
{ model: 'Road Tire Tube', price: 1421.27, sellDate: '2025-01-31', sellTime: '13:33', inStock: true },
{ model: 'Action Camera', price: 1019.05, sellDate: '2025-12-07', sellTime: '01:26', inStock: false },
{ model: 'Carbon Handlebar', price: 603.96, sellDate: '2025-09-13', sellTime: '04:10', inStock: false },
];
const container = document.getElementById('example3')!;
const hot = new Handsontable(container, {
data,
colHeaders: true,
rowHeaders: true,
columns: [
{
title: 'Model',
type: 'text',
data: 'model',
width: 150,
headerClassName: 'htLeft',
},
{
title: 'Price',
type: 'numeric',
data: 'price',
width: 80,
locale: 'en-US',
numericFormat: {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
},
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'Date',
type: 'intl-date',
data: 'sellDate',
width: 131,
locale: 'en-US',
dateFormat: { month: 'short', day: 'numeric', year: 'numeric' },
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'Time',
type: 'intl-time',
data: 'sellTime',
width: 90,
locale: 'en-US',
timeFormat: { hour: '2-digit', minute: '2-digit', hour12: true },
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'In stock',
type: 'checkbox',
data: 'inStock',
className: 'htCenter',
headerClassName: 'htCenter',
},
],
width: '100%',
height: 300,
stretchH: 'all',
dialog: {
content:
'<p>This dialog contains <strong>HTML</strong> content with formatting.</p><button type="button" class="hot-doc-dialog-html-button" id="example3-button">Hide dialog</button>',
closable: true,
},
// Required for the dialog's HTML content to be rendered safely
sanitizer: sanitizeDialogContent,
licenseKey: 'non-commercial-and-evaluation',
});
// Show dialog after initialization
const dialogPlugin = hot.getPlugin('dialog');
dialogPlugin.show();
(document.getElementById('example3-button') as HTMLElement).addEventListener('click', () => {
dialogPlugin.hide();
});

Template types

As the content option allows you to use plain text, HTML strings, and DOM elements, the template option allows you to use predefined dialog templates instead of custom content which can be useful for displaying alerts, confirmations, and other common ready-to-use dialogs.

The plugin offers two new methods to display predefined dialog templates: showAlert and showConfirm. For users looking for more customizability, the show method allows using templates with other options that the dialog offers, such as background variants, content background, and more.

JavaScript
import Handsontable from 'handsontable/base';
import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modules
registerAllModules();
const data = [
{ model: 'Trail Helmet', price: 1298.14, sellDate: '2025-08-31', sellTime: '14:12', inStock: true },
{ model: 'Windbreaker Jacket', price: 178.9, sellDate: '2025-05-10', sellTime: '22:26', inStock: false },
{ model: 'Cycling Cap', price: 288.1, sellDate: '2025-09-15', sellTime: '09:37', inStock: true },
{ model: 'HL Mountain Frame', price: 94.49, sellDate: '2025-01-17', sellTime: '14:19', inStock: false },
{ model: 'Racing Socks', price: 430.38, sellDate: '2025-05-10', sellTime: '13:42', inStock: true },
{ model: 'Racing Socks', price: 138.85, sellDate: '2025-09-20', sellTime: '14:48', inStock: true },
{ model: 'HL Mountain Frame', price: 1909.63, sellDate: '2025-09-05', sellTime: '09:35', inStock: false },
{ model: 'Carbon Handlebar', price: 1080.7, sellDate: '2025-10-24', sellTime: '22:58', inStock: false },
{ model: 'Aero Bottle', price: 1571.13, sellDate: '2025-05-24', sellTime: '00:24', inStock: true },
{ model: 'Windbreaker Jacket', price: 919.09, sellDate: '2025-07-16', sellTime: '19:11', inStock: true },
{ model: 'HL Road Tire', price: 886.22, sellDate: '2025-09-09', sellTime: '00:42', inStock: false },
{ model: 'Speed Gloves', price: 635.13, sellDate: '2025-11-17', sellTime: '12:45', inStock: true },
{ model: 'Trail Helmet', price: 1440.64, sellDate: '2025-01-03', sellTime: '20:16', inStock: false },
{ model: 'Aero Bottle', price: 944.63, sellDate: '2025-11-15', sellTime: '16:14', inStock: false },
{ model: 'Windbreaker Jacket', price: 1161.43, sellDate: '2025-06-24', sellTime: '13:19', inStock: false },
{ model: 'LED Bike Light', price: 1012.5, sellDate: '2025-05-01', sellTime: '17:30', inStock: false },
{ model: 'Windbreaker Jacket', price: 635.37, sellDate: '2025-05-14', sellTime: '09:05', inStock: true },
{ model: 'Road Tire Tube', price: 1421.27, sellDate: '2025-01-31', sellTime: '13:33', inStock: true },
{ model: 'Action Camera', price: 1019.05, sellDate: '2025-12-07', sellTime: '01:26', inStock: false },
{ model: 'Carbon Handlebar', price: 603.96, sellDate: '2025-09-13', sellTime: '04:10', inStock: false },
];
const container = document.getElementById('example4');
const hot = new Handsontable(container, {
data,
colHeaders: true,
rowHeaders: true,
columns: [
{
title: 'Model',
type: 'text',
data: 'model',
width: 150,
headerClassName: 'htLeft',
},
{
title: 'Price',
type: 'numeric',
data: 'price',
width: 80,
locale: 'en-US',
numericFormat: {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
},
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'Date',
type: 'intl-date',
data: 'sellDate',
width: 131,
locale: 'en-US',
dateFormat: { month: 'short', day: 'numeric', year: 'numeric' },
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'Time',
type: 'intl-time',
data: 'sellTime',
width: 90,
locale: 'en-US',
timeFormat: { hour: '2-digit', minute: '2-digit', hour12: true },
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'In stock',
type: 'checkbox',
data: 'inStock',
className: 'htCenter',
headerClassName: 'htCenter',
},
],
width: '100%',
height: 300,
stretchH: 'all',
dialog: true,
licenseKey: 'non-commercial-and-evaluation',
});
const dialogPlugin = hot.getPlugin('dialog');
// Add event listeners for buttons
document.getElementById('showAlert').addEventListener('click', () => {
dialogPlugin.showAlert(
{
title: 'Alert',
description: 'This is an example of the alert dialog.',
},
() => {
dialogPlugin.hide();
}
);
});
document.getElementById('showConfirm').addEventListener('click', () => {
dialogPlugin.showConfirm(
'Do you want to undo the last action?',
() => {
hot.getPlugin('undoRedo').undo();
dialogPlugin.hide();
},
() => {
dialogPlugin.hide();
}
);
});
document.getElementById('showCustomConfirm').addEventListener('click', () => {
dialogPlugin.show({
template: {
type: 'confirm',
title: 'Increase the price of the first row by:',
buttons: [
{
text: 'Cancel',
type: 'secondary',
callback: () => {
dialogPlugin.hide();
},
},
{
text: '$100',
type: 'secondary',
callback: () => {
dialogPlugin.hide();
hot.setDataAtCell(0, 1, hot.getDataAtCell(0, 1) + 100);
hot.selectCell(0, 1);
},
},
{
text: '$200',
type: 'secondary',
callback: () => {
dialogPlugin.hide();
hot.setDataAtCell(0, 1, hot.getDataAtCell(0, 1) + 200);
hot.selectCell(0, 1);
},
},
{
text: '$500',
type: 'secondary',
callback: () => {
dialogPlugin.hide();
hot.setDataAtCell(0, 1, hot.getDataAtCell(0, 1) + 500);
hot.selectCell(0, 1);
},
},
],
},
background: 'solid',
contentBackground: false,
closable: true,
});
});
TypeScript
import Handsontable from 'handsontable/base';
import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modules
registerAllModules();
const data = [
{ model: 'Trail Helmet', price: 1298.14, sellDate: '2025-08-31', sellTime: '14:12', inStock: true },
{ model: 'Windbreaker Jacket', price: 178.9, sellDate: '2025-05-10', sellTime: '22:26', inStock: false },
{ model: 'Cycling Cap', price: 288.1, sellDate: '2025-09-15', sellTime: '09:37', inStock: true },
{ model: 'HL Mountain Frame', price: 94.49, sellDate: '2025-01-17', sellTime: '14:19', inStock: false },
{ model: 'Racing Socks', price: 430.38, sellDate: '2025-05-10', sellTime: '13:42', inStock: true },
{ model: 'Racing Socks', price: 138.85, sellDate: '2025-09-20', sellTime: '14:48', inStock: true },
{ model: 'HL Mountain Frame', price: 1909.63, sellDate: '2025-09-05', sellTime: '09:35', inStock: false },
{ model: 'Carbon Handlebar', price: 1080.7, sellDate: '2025-10-24', sellTime: '22:58', inStock: false },
{ model: 'Aero Bottle', price: 1571.13, sellDate: '2025-05-24', sellTime: '00:24', inStock: true },
{ model: 'Windbreaker Jacket', price: 919.09, sellDate: '2025-07-16', sellTime: '19:11', inStock: true },
{ model: 'HL Road Tire', price: 886.22, sellDate: '2025-09-09', sellTime: '00:42', inStock: false },
{ model: 'Speed Gloves', price: 635.13, sellDate: '2025-11-17', sellTime: '12:45', inStock: true },
{ model: 'Trail Helmet', price: 1440.64, sellDate: '2025-01-03', sellTime: '20:16', inStock: false },
{ model: 'Aero Bottle', price: 944.63, sellDate: '2025-11-15', sellTime: '16:14', inStock: false },
{ model: 'Windbreaker Jacket', price: 1161.43, sellDate: '2025-06-24', sellTime: '13:19', inStock: false },
{ model: 'LED Bike Light', price: 1012.5, sellDate: '2025-05-01', sellTime: '17:30', inStock: false },
{ model: 'Windbreaker Jacket', price: 635.37, sellDate: '2025-05-14', sellTime: '09:05', inStock: true },
{ model: 'Road Tire Tube', price: 1421.27, sellDate: '2025-01-31', sellTime: '13:33', inStock: true },
{ model: 'Action Camera', price: 1019.05, sellDate: '2025-12-07', sellTime: '01:26', inStock: false },
{ model: 'Carbon Handlebar', price: 603.96, sellDate: '2025-09-13', sellTime: '04:10', inStock: false },
];
const container = document.getElementById('example4')!;
const hot = new Handsontable(container, {
data,
colHeaders: true,
rowHeaders: true,
columns: [
{
title: 'Model',
type: 'text',
data: 'model',
width: 150,
headerClassName: 'htLeft',
},
{
title: 'Price',
type: 'numeric',
data: 'price',
width: 80,
locale: 'en-US',
numericFormat: {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
},
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'Date',
type: 'intl-date',
data: 'sellDate',
width: 131,
locale: 'en-US',
dateFormat: { month: 'short', day: 'numeric', year: 'numeric' },
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'Time',
type: 'intl-time',
data: 'sellTime',
width: 90,
locale: 'en-US',
timeFormat: { hour: '2-digit', minute: '2-digit', hour12: true },
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'In stock',
type: 'checkbox',
data: 'inStock',
className: 'htCenter',
headerClassName: 'htCenter',
},
],
width: '100%',
height: 300,
stretchH: 'all',
dialog: true,
licenseKey: 'non-commercial-and-evaluation',
});
const dialogPlugin = hot.getPlugin('dialog');
// Add event listeners for buttons
document.getElementById('showAlert')!.addEventListener('click', () => {
dialogPlugin.showAlert(
{
title: 'Alert',
description: 'This is an example of the alert dialog.',
},
() => {
dialogPlugin.hide();
}
);
});
document.getElementById('showConfirm')!.addEventListener('click', () => {
dialogPlugin.showConfirm(
'Do you want to undo the last action?',
() => {
hot.getPlugin('undoRedo').undo();
dialogPlugin.hide();
},
() => {
dialogPlugin.hide();
}
);
});
document.getElementById('showCustomConfirm')!.addEventListener('click', () => {
dialogPlugin.show({
template: {
type: 'confirm',
title: 'Increase the price of the first row by:',
buttons: [
{
text: 'Cancel',
type: 'secondary',
callback: () => {
dialogPlugin.hide();
},
},
{
text: '$100',
type: 'secondary',
callback: () => {
dialogPlugin.hide();
hot.setDataAtCell(0, 1, hot.getDataAtCell(0, 1) + 100);
hot.selectCell(0, 1);
},
},
{
text: '$200',
type: 'secondary',
callback: () => {
dialogPlugin.hide();
hot.setDataAtCell(0, 1, hot.getDataAtCell(0, 1) + 200);
hot.selectCell(0, 1);
},
},
{
text: '$500',
type: 'secondary',
callback: () => {
dialogPlugin.hide();
hot.setDataAtCell(0, 1, hot.getDataAtCell(0, 1) + 500);
hot.selectCell(0, 1);
},
},
],
},
background: 'solid',
contentBackground: false,
closable: true,
});
});
HTML
<div class="example-controls-container">
<div class="controls">
<button type="button" id="showAlert">Show Alert</button>
<button type="button" id="showConfirm">Show Confirm</button>
<button type="button" id="showCustomConfirm">Show custom Confirm (with solid background)</button>
</div>
</div>
<div id="example4"></div>

Background variants

The dialog supports two background variants: solid and semi-transparent.

JavaScript
import Handsontable from 'handsontable/base';
import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modules
registerAllModules();
const data = [
{ model: 'Trail Helmet', price: 1298.14, sellDate: '2025-08-31', sellTime: '14:12', inStock: true },
{ model: 'Windbreaker Jacket', price: 178.9, sellDate: '2025-05-10', sellTime: '22:26', inStock: false },
{ model: 'Cycling Cap', price: 288.1, sellDate: '2025-09-15', sellTime: '09:37', inStock: true },
{ model: 'HL Mountain Frame', price: 94.49, sellDate: '2025-01-17', sellTime: '14:19', inStock: false },
{ model: 'Racing Socks', price: 430.38, sellDate: '2025-05-10', sellTime: '13:42', inStock: true },
{ model: 'Racing Socks', price: 138.85, sellDate: '2025-09-20', sellTime: '14:48', inStock: true },
{ model: 'HL Mountain Frame', price: 1909.63, sellDate: '2025-09-05', sellTime: '09:35', inStock: false },
{ model: 'Carbon Handlebar', price: 1080.7, sellDate: '2025-10-24', sellTime: '22:58', inStock: false },
{ model: 'Aero Bottle', price: 1571.13, sellDate: '2025-05-24', sellTime: '00:24', inStock: true },
{ model: 'Windbreaker Jacket', price: 919.09, sellDate: '2025-07-16', sellTime: '19:11', inStock: true },
{ model: 'HL Road Tire', price: 886.22, sellDate: '2025-09-09', sellTime: '00:42', inStock: false },
{ model: 'Speed Gloves', price: 635.13, sellDate: '2025-11-17', sellTime: '12:45', inStock: true },
{ model: 'Trail Helmet', price: 1440.64, sellDate: '2025-01-03', sellTime: '20:16', inStock: false },
{ model: 'Aero Bottle', price: 944.63, sellDate: '2025-11-15', sellTime: '16:14', inStock: false },
{ model: 'Windbreaker Jacket', price: 1161.43, sellDate: '2025-06-24', sellTime: '13:19', inStock: false },
{ model: 'LED Bike Light', price: 1012.5, sellDate: '2025-05-01', sellTime: '17:30', inStock: false },
{ model: 'Windbreaker Jacket', price: 635.37, sellDate: '2025-05-14', sellTime: '09:05', inStock: true },
{ model: 'Road Tire Tube', price: 1421.27, sellDate: '2025-01-31', sellTime: '13:33', inStock: true },
{ model: 'Action Camera', price: 1019.05, sellDate: '2025-12-07', sellTime: '01:26', inStock: false },
{ model: 'Carbon Handlebar', price: 603.96, sellDate: '2025-09-13', sellTime: '04:10', inStock: false },
];
const container = document.getElementById('example5');
const hot = new Handsontable(container, {
data,
colHeaders: true,
rowHeaders: true,
columns: [
{
title: 'Model',
type: 'text',
data: 'model',
width: 150,
headerClassName: 'htLeft',
},
{
title: 'Price',
type: 'numeric',
data: 'price',
width: 80,
locale: 'en-US',
numericFormat: {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
},
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'Date',
type: 'intl-date',
data: 'sellDate',
width: 131,
locale: 'en-US',
dateFormat: { month: 'short', day: 'numeric', year: 'numeric' },
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'Time',
type: 'intl-time',
data: 'sellTime',
width: 90,
locale: 'en-US',
timeFormat: { hour: '2-digit', minute: '2-digit', hour12: true },
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'In stock',
type: 'checkbox',
data: 'inStock',
className: 'htCenter',
headerClassName: 'htCenter',
},
],
width: '100%',
height: 300,
stretchH: 'all',
dialog: {
content: 'This dialog uses a solid background (default).',
background: 'solid',
closable: true,
},
licenseKey: 'non-commercial-and-evaluation',
});
// Show dialog after initialization
const dialogPlugin = hot.getPlugin('dialog');
dialogPlugin.show();
// Custom dropdown logic
const dropdown = document.getElementById('backgroundDropdown');
const trigger = document.getElementById('backgroundTrigger');
const menu = document.getElementById('backgroundMenu');
const label = document.getElementById('backgroundLabel');
trigger.addEventListener('click', () => {
const isOpen = !menu.hidden;
menu.hidden = isOpen;
trigger.setAttribute('aria-expanded', String(!isOpen));
});
menu.addEventListener('click', (e) => {
const item = e.target.closest('li[data-value]');
if (item) {
label.textContent = item.textContent.trim();
menu.querySelectorAll('li').forEach((li) => li.setAttribute('aria-selected', 'false'));
item.setAttribute('aria-selected', 'true');
menu.hidden = true;
trigger.setAttribute('aria-expanded', 'false');
const background = item.dataset.value;
const content =
background === 'solid'
? 'This dialog uses a solid background (default).'
: 'This dialog uses a semi-transparent background.';
dialogPlugin.update({ content, background });
}
});
document.addEventListener('click', (e) => {
if (!dropdown.contains(e.target)) {
menu.hidden = true;
trigger.setAttribute('aria-expanded', 'false');
}
});
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && !menu.hidden) {
menu.hidden = true;
trigger.setAttribute('aria-expanded', 'false');
trigger.focus();
}
});
TypeScript
import Handsontable from 'handsontable/base';
import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modules
registerAllModules();
const data = [
{ model: 'Trail Helmet', price: 1298.14, sellDate: '2025-08-31', sellTime: '14:12', inStock: true },
{ model: 'Windbreaker Jacket', price: 178.9, sellDate: '2025-05-10', sellTime: '22:26', inStock: false },
{ model: 'Cycling Cap', price: 288.1, sellDate: '2025-09-15', sellTime: '09:37', inStock: true },
{ model: 'HL Mountain Frame', price: 94.49, sellDate: '2025-01-17', sellTime: '14:19', inStock: false },
{ model: 'Racing Socks', price: 430.38, sellDate: '2025-05-10', sellTime: '13:42', inStock: true },
{ model: 'Racing Socks', price: 138.85, sellDate: '2025-09-20', sellTime: '14:48', inStock: true },
{ model: 'HL Mountain Frame', price: 1909.63, sellDate: '2025-09-05', sellTime: '09:35', inStock: false },
{ model: 'Carbon Handlebar', price: 1080.7, sellDate: '2025-10-24', sellTime: '22:58', inStock: false },
{ model: 'Aero Bottle', price: 1571.13, sellDate: '2025-05-24', sellTime: '00:24', inStock: true },
{ model: 'Windbreaker Jacket', price: 919.09, sellDate: '2025-07-16', sellTime: '19:11', inStock: true },
{ model: 'HL Road Tire', price: 886.22, sellDate: '2025-09-09', sellTime: '00:42', inStock: false },
{ model: 'Speed Gloves', price: 635.13, sellDate: '2025-11-17', sellTime: '12:45', inStock: true },
{ model: 'Trail Helmet', price: 1440.64, sellDate: '2025-01-03', sellTime: '20:16', inStock: false },
{ model: 'Aero Bottle', price: 944.63, sellDate: '2025-11-15', sellTime: '16:14', inStock: false },
{ model: 'Windbreaker Jacket', price: 1161.43, sellDate: '2025-06-24', sellTime: '13:19', inStock: false },
{ model: 'LED Bike Light', price: 1012.5, sellDate: '2025-05-01', sellTime: '17:30', inStock: false },
{ model: 'Windbreaker Jacket', price: 635.37, sellDate: '2025-05-14', sellTime: '09:05', inStock: true },
{ model: 'Road Tire Tube', price: 1421.27, sellDate: '2025-01-31', sellTime: '13:33', inStock: true },
{ model: 'Action Camera', price: 1019.05, sellDate: '2025-12-07', sellTime: '01:26', inStock: false },
{ model: 'Carbon Handlebar', price: 603.96, sellDate: '2025-09-13', sellTime: '04:10', inStock: false },
];
const container = document.getElementById('example5')!;
const hot = new Handsontable(container, {
data,
colHeaders: true,
rowHeaders: true,
columns: [
{
title: 'Model',
type: 'text',
data: 'model',
width: 150,
headerClassName: 'htLeft',
},
{
title: 'Price',
type: 'numeric',
data: 'price',
width: 80,
locale: 'en-US',
numericFormat: {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
},
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'Date',
type: 'intl-date',
data: 'sellDate',
width: 131,
locale: 'en-US',
dateFormat: { month: 'short', day: 'numeric', year: 'numeric' },
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'Time',
type: 'intl-time',
data: 'sellTime',
width: 90,
locale: 'en-US',
timeFormat: { hour: '2-digit', minute: '2-digit', hour12: true },
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'In stock',
type: 'checkbox',
data: 'inStock',
className: 'htCenter',
headerClassName: 'htCenter',
},
],
width: '100%',
height: 300,
stretchH: 'all',
dialog: {
content: 'This dialog uses a solid background (default).',
background: 'solid',
closable: true,
},
licenseKey: 'non-commercial-and-evaluation',
});
// Show dialog after initialization
const dialogPlugin = hot.getPlugin('dialog');
dialogPlugin.show();
// Custom dropdown logic
const dropdown = document.getElementById('backgroundDropdown')!;
const trigger = document.getElementById('backgroundTrigger')!;
const menu = document.getElementById('backgroundMenu')!;
const label = document.getElementById('backgroundLabel')!;
trigger.addEventListener('click', () => {
const isOpen = !menu.hidden;
menu.hidden = isOpen;
trigger.setAttribute('aria-expanded', String(!isOpen));
});
menu.addEventListener('click', (e: MouseEvent) => {
const item = (e.target as HTMLElement).closest('li[data-value]') as HTMLElement | null;
if (item) {
label.textContent = item.textContent!.trim();
menu.querySelectorAll('li').forEach((li) => li.setAttribute('aria-selected', 'false'));
item.setAttribute('aria-selected', 'true');
menu.hidden = true;
trigger.setAttribute('aria-expanded', 'false');
const background = item.dataset.value!;
const content =
background === 'solid'
? 'This dialog uses a solid background (default).'
: 'This dialog uses a semi-transparent background.';
dialogPlugin.update({ content, background });
}
});
document.addEventListener('click', (e: MouseEvent) => {
if (!dropdown.contains(e.target as Node)) {
menu.hidden = true;
trigger.setAttribute('aria-expanded', 'false');
}
});
document.addEventListener('keydown', (e: KeyboardEvent) => {
if (e.key === 'Escape' && !menu.hidden) {
menu.hidden = true;
trigger.setAttribute('aria-expanded', 'false');
trigger.focus();
}
});
HTML
<div class="example-controls-container">
<div class="controls">
<div class="theme-dropdown" id="backgroundDropdown">
<button class="theme-dropdown-trigger" id="backgroundTrigger" type="button" aria-haspopup="listbox" aria-expanded="false">
<span id="backgroundLabel">Solid</span>
<svg class="theme-dropdown-chevron" aria-hidden="true" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 9l6 6l6 -6"/></svg>
</button>
<ul class="theme-dropdown-menu" id="backgroundMenu" role="listbox" hidden>
<li role="option" data-value="solid" aria-selected="true">Solid</li>
<li role="option" data-value="semi-transparent">Semi-transparent</li>
</ul>
</div>
</div>
</div>
<div id="example5"></div>

Content background

The dialog content can have a background color using the contentBackground option.

JavaScript
import Handsontable from 'handsontable/base';
import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modules
registerAllModules();
const data = [
{ model: 'Trail Helmet', price: 1298.14, sellDate: '2025-08-31', sellTime: '14:12', inStock: true },
{ model: 'Windbreaker Jacket', price: 178.9, sellDate: '2025-05-10', sellTime: '22:26', inStock: false },
{ model: 'Cycling Cap', price: 288.1, sellDate: '2025-09-15', sellTime: '09:37', inStock: true },
{ model: 'HL Mountain Frame', price: 94.49, sellDate: '2025-01-17', sellTime: '14:19', inStock: false },
{ model: 'Racing Socks', price: 430.38, sellDate: '2025-05-10', sellTime: '13:42', inStock: true },
{ model: 'Racing Socks', price: 138.85, sellDate: '2025-09-20', sellTime: '14:48', inStock: true },
{ model: 'HL Mountain Frame', price: 1909.63, sellDate: '2025-09-05', sellTime: '09:35', inStock: false },
{ model: 'Carbon Handlebar', price: 1080.7, sellDate: '2025-10-24', sellTime: '22:58', inStock: false },
{ model: 'Aero Bottle', price: 1571.13, sellDate: '2025-05-24', sellTime: '00:24', inStock: true },
{ model: 'Windbreaker Jacket', price: 919.09, sellDate: '2025-07-16', sellTime: '19:11', inStock: true },
{ model: 'HL Road Tire', price: 886.22, sellDate: '2025-09-09', sellTime: '00:42', inStock: false },
{ model: 'Speed Gloves', price: 635.13, sellDate: '2025-11-17', sellTime: '12:45', inStock: true },
{ model: 'Trail Helmet', price: 1440.64, sellDate: '2025-01-03', sellTime: '20:16', inStock: false },
{ model: 'Aero Bottle', price: 944.63, sellDate: '2025-11-15', sellTime: '16:14', inStock: false },
{ model: 'Windbreaker Jacket', price: 1161.43, sellDate: '2025-06-24', sellTime: '13:19', inStock: false },
{ model: 'LED Bike Light', price: 1012.5, sellDate: '2025-05-01', sellTime: '17:30', inStock: false },
{ model: 'Windbreaker Jacket', price: 635.37, sellDate: '2025-05-14', sellTime: '09:05', inStock: true },
{ model: 'Road Tire Tube', price: 1421.27, sellDate: '2025-01-31', sellTime: '13:33', inStock: true },
{ model: 'Action Camera', price: 1019.05, sellDate: '2025-12-07', sellTime: '01:26', inStock: false },
{ model: 'Carbon Handlebar', price: 603.96, sellDate: '2025-09-13', sellTime: '04:10', inStock: false },
];
const container = document.getElementById('example6');
const hot = new Handsontable(container, {
data,
colHeaders: true,
rowHeaders: true,
columns: [
{
title: 'Model',
type: 'text',
data: 'model',
width: 150,
headerClassName: 'htLeft',
},
{
title: 'Price',
type: 'numeric',
data: 'price',
width: 80,
locale: 'en-US',
numericFormat: {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
},
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'Date',
type: 'intl-date',
data: 'sellDate',
width: 131,
locale: 'en-US',
dateFormat: { month: 'short', day: 'numeric', year: 'numeric' },
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'Time',
type: 'intl-time',
data: 'sellTime',
width: 90,
locale: 'en-US',
timeFormat: { hour: '2-digit', minute: '2-digit', hour12: true },
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'In stock',
type: 'checkbox',
data: 'inStock',
className: 'htCenter',
headerClassName: 'htCenter',
},
],
width: '100%',
height: 300,
stretchH: 'all',
dialog: {
content: 'This dialog uses a semi-transparent background.',
contentBackground: true,
background: 'semi-transparent',
closable: true,
},
licenseKey: 'non-commercial-and-evaluation',
});
// Show dialog after initialization
const dialogPlugin = hot.getPlugin('dialog');
dialogPlugin.show();
TypeScript
import Handsontable from 'handsontable/base';
import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modules
registerAllModules();
const data = [
{ model: 'Trail Helmet', price: 1298.14, sellDate: '2025-08-31', sellTime: '14:12', inStock: true },
{ model: 'Windbreaker Jacket', price: 178.9, sellDate: '2025-05-10', sellTime: '22:26', inStock: false },
{ model: 'Cycling Cap', price: 288.1, sellDate: '2025-09-15', sellTime: '09:37', inStock: true },
{ model: 'HL Mountain Frame', price: 94.49, sellDate: '2025-01-17', sellTime: '14:19', inStock: false },
{ model: 'Racing Socks', price: 430.38, sellDate: '2025-05-10', sellTime: '13:42', inStock: true },
{ model: 'Racing Socks', price: 138.85, sellDate: '2025-09-20', sellTime: '14:48', inStock: true },
{ model: 'HL Mountain Frame', price: 1909.63, sellDate: '2025-09-05', sellTime: '09:35', inStock: false },
{ model: 'Carbon Handlebar', price: 1080.7, sellDate: '2025-10-24', sellTime: '22:58', inStock: false },
{ model: 'Aero Bottle', price: 1571.13, sellDate: '2025-05-24', sellTime: '00:24', inStock: true },
{ model: 'Windbreaker Jacket', price: 919.09, sellDate: '2025-07-16', sellTime: '19:11', inStock: true },
{ model: 'HL Road Tire', price: 886.22, sellDate: '2025-09-09', sellTime: '00:42', inStock: false },
{ model: 'Speed Gloves', price: 635.13, sellDate: '2025-11-17', sellTime: '12:45', inStock: true },
{ model: 'Trail Helmet', price: 1440.64, sellDate: '2025-01-03', sellTime: '20:16', inStock: false },
{ model: 'Aero Bottle', price: 944.63, sellDate: '2025-11-15', sellTime: '16:14', inStock: false },
{ model: 'Windbreaker Jacket', price: 1161.43, sellDate: '2025-06-24', sellTime: '13:19', inStock: false },
{ model: 'LED Bike Light', price: 1012.5, sellDate: '2025-05-01', sellTime: '17:30', inStock: false },
{ model: 'Windbreaker Jacket', price: 635.37, sellDate: '2025-05-14', sellTime: '09:05', inStock: true },
{ model: 'Road Tire Tube', price: 1421.27, sellDate: '2025-01-31', sellTime: '13:33', inStock: true },
{ model: 'Action Camera', price: 1019.05, sellDate: '2025-12-07', sellTime: '01:26', inStock: false },
{ model: 'Carbon Handlebar', price: 603.96, sellDate: '2025-09-13', sellTime: '04:10', inStock: false },
];
const container = document.getElementById('example6')!;
const hot = new Handsontable(container, {
data,
colHeaders: true,
rowHeaders: true,
columns: [
{
title: 'Model',
type: 'text',
data: 'model',
width: 150,
headerClassName: 'htLeft',
},
{
title: 'Price',
type: 'numeric',
data: 'price',
width: 80,
locale: 'en-US',
numericFormat: {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
},
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'Date',
type: 'intl-date',
data: 'sellDate',
width: 131,
locale: 'en-US',
dateFormat: { month: 'short', day: 'numeric', year: 'numeric' },
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'Time',
type: 'intl-time',
data: 'sellTime',
width: 90,
locale: 'en-US',
timeFormat: { hour: '2-digit', minute: '2-digit', hour12: true },
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'In stock',
type: 'checkbox',
data: 'inStock',
className: 'htCenter',
headerClassName: 'htCenter',
},
],
width: '100%',
height: 300,
stretchH: 'all',
dialog: {
content: 'This dialog uses a semi-transparent background.',
contentBackground: true,
background: 'semi-transparent',
closable: true,
},
licenseKey: 'non-commercial-and-evaluation',
});
// Show dialog after initialization
const dialogPlugin = hot.getPlugin('dialog');
dialogPlugin.show();

Dialog accessibility

The dialog plugin provides accessibility features through ARIA attributes. You can configure the dialog’s accessibility properties using the a11y option, which includes:

  • role - Sets the ARIA role (defaults to “dialog”)
  • ariaLabel - Sets the dialog’s accessible name (defaults to “Dialog”). This is used when there is no visible dialog title that can be referenced by ariaLabelledby. If both ariaLabel and ariaLabelledby are provided, ariaLabelledby takes precedence
  • ariaLabelledby - References an element that labels the dialog
  • ariaDescribedby - References an element that describes the dialog
JavaScript
import Handsontable from 'handsontable/base';
import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modules
registerAllModules();
const ALLOWED_TAGS = ['H2', 'P', 'STRONG', 'BUTTON', 'BR', 'TABLE', 'THEAD', 'TBODY', 'TR', 'TD', 'TH'];
const ALLOWED_ATTRIBUTES = ['id', 'class', 'type', 'colspan', 'rowspan'];
const DROPPED_TAGS = ['SCRIPT', 'STYLE', 'TEXTAREA', 'TITLE'];
// Handsontable has no built-in sanitizer since v18.0, and `sanitizer` is grid-level:
// it also filters pasted HTML, so the table tags have to survive -- otherwise pasting
// a range degrades to plain text. In production, use a vetted library such as DOMPurify.
// See https://handsontable.com/docs/security/
const sanitizeDialogContent = (html) => {
const template = document.createElement('template');
template.innerHTML = html;
template.content.querySelectorAll('*').forEach((element) => {
if (DROPPED_TAGS.includes(element.tagName)) {
// Unwrapping these would promote their source text into the output
element.remove();
} else if (ALLOWED_TAGS.includes(element.tagName)) {
Array.from(element.attributes).forEach((attribute) => {
if (!ALLOWED_ATTRIBUTES.includes(attribute.name)) {
element.removeAttribute(attribute.name);
}
});
} else {
// Unwrap a disallowed element, keeping its text content
element.replaceWith(...Array.from(element.childNodes));
}
});
return template.innerHTML;
};
const data = [
{ model: 'Trail Helmet', price: 1298.14, sellDate: '2025-08-31', sellTime: '14:12', inStock: true },
{ model: 'Windbreaker Jacket', price: 178.9, sellDate: '2025-05-10', sellTime: '22:26', inStock: false },
{ model: 'Cycling Cap', price: 288.1, sellDate: '2025-09-15', sellTime: '09:37', inStock: true },
{ model: 'HL Mountain Frame', price: 94.49, sellDate: '2025-01-17', sellTime: '14:19', inStock: false },
{ model: 'Racing Socks', price: 430.38, sellDate: '2025-05-10', sellTime: '13:42', inStock: true },
{ model: 'Racing Socks', price: 138.85, sellDate: '2025-09-20', sellTime: '14:48', inStock: true },
{ model: 'HL Mountain Frame', price: 1909.63, sellDate: '2025-09-05', sellTime: '09:35', inStock: false },
{ model: 'Carbon Handlebar', price: 1080.7, sellDate: '2025-10-24', sellTime: '22:58', inStock: false },
{ model: 'Aero Bottle', price: 1571.13, sellDate: '2025-05-24', sellTime: '00:24', inStock: true },
{ model: 'Windbreaker Jacket', price: 919.09, sellDate: '2025-07-16', sellTime: '19:11', inStock: true },
{ model: 'HL Road Tire', price: 886.22, sellDate: '2025-09-09', sellTime: '00:42', inStock: false },
{ model: 'Speed Gloves', price: 635.13, sellDate: '2025-11-17', sellTime: '12:45', inStock: true },
{ model: 'Trail Helmet', price: 1440.64, sellDate: '2025-01-03', sellTime: '20:16', inStock: false },
{ model: 'Aero Bottle', price: 944.63, sellDate: '2025-11-15', sellTime: '16:14', inStock: false },
{ model: 'Windbreaker Jacket', price: 1161.43, sellDate: '2025-06-24', sellTime: '13:19', inStock: false },
{ model: 'LED Bike Light', price: 1012.5, sellDate: '2025-05-01', sellTime: '17:30', inStock: false },
{ model: 'Windbreaker Jacket', price: 635.37, sellDate: '2025-05-14', sellTime: '09:05', inStock: true },
{ model: 'Road Tire Tube', price: 1421.27, sellDate: '2025-01-31', sellTime: '13:33', inStock: true },
{ model: 'Action Camera', price: 1019.05, sellDate: '2025-12-07', sellTime: '01:26', inStock: false },
{ model: 'Carbon Handlebar', price: 603.96, sellDate: '2025-09-13', sellTime: '04:10', inStock: false },
];
const container = document.getElementById('example7');
const hot = new Handsontable(container, {
data,
colHeaders: true,
rowHeaders: true,
columns: [
{
title: 'Model',
type: 'text',
data: 'model',
width: 150,
headerClassName: 'htLeft',
},
{
title: 'Price',
type: 'numeric',
data: 'price',
width: 80,
locale: 'en-US',
numericFormat: {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
},
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'Date',
type: 'intl-date',
data: 'sellDate',
width: 131,
locale: 'en-US',
dateFormat: { month: 'short', day: 'numeric', year: 'numeric' },
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'Time',
type: 'intl-time',
data: 'sellTime',
width: 90,
locale: 'en-US',
timeFormat: { hour: '2-digit', minute: '2-digit', hour12: true },
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'In stock',
type: 'checkbox',
data: 'inStock',
className: 'htCenter',
headerClassName: 'htCenter',
},
],
width: '100%',
height: 300,
stretchH: 'all',
dialog: {
content: '<h2 id="example6-title">Title</h2><p id="example6-description">Description</p>',
a11y: {
role: 'alertdialog',
ariaLabel: 'Title',
ariaLabelledby: 'example6-title',
ariaDescribedby: 'example6-description',
},
closable: true,
},
// Required for the dialog's HTML content to be rendered safely
sanitizer: sanitizeDialogContent,
licenseKey: 'non-commercial-and-evaluation',
});
// Show dialog after initialization
const dialogPlugin = hot.getPlugin('dialog');
dialogPlugin.show();
TypeScript
import Handsontable from 'handsontable/base';
import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modules
registerAllModules();
const ALLOWED_TAGS = ['H2', 'P', 'STRONG', 'BUTTON', 'BR', 'TABLE', 'THEAD', 'TBODY', 'TR', 'TD', 'TH'];
const ALLOWED_ATTRIBUTES = ['id', 'class', 'type', 'colspan', 'rowspan'];
const DROPPED_TAGS = ['SCRIPT', 'STYLE', 'TEXTAREA', 'TITLE'];
// Handsontable has no built-in sanitizer since v18.0, and `sanitizer` is grid-level:
// it also filters pasted HTML, so the table tags have to survive -- otherwise pasting
// a range degrades to plain text. In production, use a vetted library such as DOMPurify.
// See https://handsontable.com/docs/security/
const sanitizeDialogContent = (html: string): string => {
const template = document.createElement('template');
template.innerHTML = html;
template.content.querySelectorAll('*').forEach((element) => {
if (DROPPED_TAGS.includes(element.tagName)) {
// Unwrapping these would promote their source text into the output
element.remove();
} else if (ALLOWED_TAGS.includes(element.tagName)) {
Array.from(element.attributes).forEach((attribute) => {
if (!ALLOWED_ATTRIBUTES.includes(attribute.name)) {
element.removeAttribute(attribute.name);
}
});
} else {
// Unwrap a disallowed element, keeping its text content
element.replaceWith(...Array.from(element.childNodes));
}
});
return template.innerHTML;
};
const data = [
{ model: 'Trail Helmet', price: 1298.14, sellDate: '2025-08-31', sellTime: '14:12', inStock: true },
{ model: 'Windbreaker Jacket', price: 178.9, sellDate: '2025-05-10', sellTime: '22:26', inStock: false },
{ model: 'Cycling Cap', price: 288.1, sellDate: '2025-09-15', sellTime: '09:37', inStock: true },
{ model: 'HL Mountain Frame', price: 94.49, sellDate: '2025-01-17', sellTime: '14:19', inStock: false },
{ model: 'Racing Socks', price: 430.38, sellDate: '2025-05-10', sellTime: '13:42', inStock: true },
{ model: 'Racing Socks', price: 138.85, sellDate: '2025-09-20', sellTime: '14:48', inStock: true },
{ model: 'HL Mountain Frame', price: 1909.63, sellDate: '2025-09-05', sellTime: '09:35', inStock: false },
{ model: 'Carbon Handlebar', price: 1080.7, sellDate: '2025-10-24', sellTime: '22:58', inStock: false },
{ model: 'Aero Bottle', price: 1571.13, sellDate: '2025-05-24', sellTime: '00:24', inStock: true },
{ model: 'Windbreaker Jacket', price: 919.09, sellDate: '2025-07-16', sellTime: '19:11', inStock: true },
{ model: 'HL Road Tire', price: 886.22, sellDate: '2025-09-09', sellTime: '00:42', inStock: false },
{ model: 'Speed Gloves', price: 635.13, sellDate: '2025-11-17', sellTime: '12:45', inStock: true },
{ model: 'Trail Helmet', price: 1440.64, sellDate: '2025-01-03', sellTime: '20:16', inStock: false },
{ model: 'Aero Bottle', price: 944.63, sellDate: '2025-11-15', sellTime: '16:14', inStock: false },
{ model: 'Windbreaker Jacket', price: 1161.43, sellDate: '2025-06-24', sellTime: '13:19', inStock: false },
{ model: 'LED Bike Light', price: 1012.5, sellDate: '2025-05-01', sellTime: '17:30', inStock: false },
{ model: 'Windbreaker Jacket', price: 635.37, sellDate: '2025-05-14', sellTime: '09:05', inStock: true },
{ model: 'Road Tire Tube', price: 1421.27, sellDate: '2025-01-31', sellTime: '13:33', inStock: true },
{ model: 'Action Camera', price: 1019.05, sellDate: '2025-12-07', sellTime: '01:26', inStock: false },
{ model: 'Carbon Handlebar', price: 603.96, sellDate: '2025-09-13', sellTime: '04:10', inStock: false },
];
const container = document.getElementById('example7')!;
const hot = new Handsontable(container, {
data,
colHeaders: true,
rowHeaders: true,
columns: [
{
title: 'Model',
type: 'text',
data: 'model',
width: 150,
headerClassName: 'htLeft',
},
{
title: 'Price',
type: 'numeric',
data: 'price',
width: 80,
locale: 'en-US',
numericFormat: {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
},
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'Date',
type: 'intl-date',
data: 'sellDate',
width: 131,
locale: 'en-US',
dateFormat: { month: 'short', day: 'numeric', year: 'numeric' },
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'Time',
type: 'intl-time',
data: 'sellTime',
width: 90,
locale: 'en-US',
timeFormat: { hour: '2-digit', minute: '2-digit', hour12: true },
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'In stock',
type: 'checkbox',
data: 'inStock',
className: 'htCenter',
headerClassName: 'htCenter',
},
],
width: '100%',
height: 300,
stretchH: 'all',
dialog: {
content: '<h2 id="example6-title">Title</h2><p id="example6-description">Description</p>',
a11y: {
role: 'alertdialog',
ariaLabel: 'Title',
ariaLabelledby: 'example6-title',
ariaDescribedby: 'example6-description',
},
closable: true,
},
// Required for the dialog's HTML content to be rendered safely
sanitizer: sanitizeDialogContent,
licenseKey: 'non-commercial-and-evaluation',
});
// Show dialog after initialization
const dialogPlugin = hot.getPlugin('dialog');
dialogPlugin.show();

Keyboard navigation and focus

When a dialog opens, focus moves into it. Where it lands depends on how you configured the dialog:

  • With the confirm template, focus moves to the first button. If the template has no buttons, focus moves to the dialog’s content area.
  • With the content option, focus moves to the dialog box itself. Handsontable does not search your HTML for focusable elements, so it never moves focus to a button inside your content. To place focus yourself, listen to the afterDialogFocus hook and call focus() on the element you want. If you also use the Loading plugin, note that it moves focus to the dialog box on that same hook, so register your callback after it or move focus on a later task.

Focus moves only if the grid was focused when the dialog opened. If the user has not interacted with the grid yet, the dialog opens without taking focus away from the rest of the page. The afterDialogFocus hook follows the same rule, so it does not run in that case either.

While the dialog is open, Tab and Shift+Tab move between the focusable elements inside it and skip the grid. They still reach the other elements on your page. Escape closes the dialog, but only when closable is set to true. The showAlert() and showConfirm() methods set closable to false, so Escape does not close those dialogs.

To move focus to the dialog box at any time, call the focus() method.

Programmatic control

You can control the dialog programmatically using the plugin’s methods.

Show and hide dialog

JavaScript
import Handsontable from 'handsontable/base';
import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modules
registerAllModules();
const data = [
{ model: 'Trail Helmet', price: 1298.14, sellDate: '2025-08-31', sellTime: '14:12', inStock: true },
{ model: 'Windbreaker Jacket', price: 178.9, sellDate: '2025-05-10', sellTime: '22:26', inStock: false },
{ model: 'Cycling Cap', price: 288.1, sellDate: '2025-09-15', sellTime: '09:37', inStock: true },
{ model: 'HL Mountain Frame', price: 94.49, sellDate: '2025-01-17', sellTime: '14:19', inStock: false },
{ model: 'Racing Socks', price: 430.38, sellDate: '2025-05-10', sellTime: '13:42', inStock: true },
{ model: 'Racing Socks', price: 138.85, sellDate: '2025-09-20', sellTime: '14:48', inStock: true },
{ model: 'HL Mountain Frame', price: 1909.63, sellDate: '2025-09-05', sellTime: '09:35', inStock: false },
{ model: 'Carbon Handlebar', price: 1080.7, sellDate: '2025-10-24', sellTime: '22:58', inStock: false },
{ model: 'Aero Bottle', price: 1571.13, sellDate: '2025-05-24', sellTime: '00:24', inStock: true },
{ model: 'Windbreaker Jacket', price: 919.09, sellDate: '2025-07-16', sellTime: '19:11', inStock: true },
{ model: 'HL Road Tire', price: 886.22, sellDate: '2025-09-09', sellTime: '00:42', inStock: false },
{ model: 'Speed Gloves', price: 635.13, sellDate: '2025-11-17', sellTime: '12:45', inStock: true },
{ model: 'Trail Helmet', price: 1440.64, sellDate: '2025-01-03', sellTime: '20:16', inStock: false },
{ model: 'Aero Bottle', price: 944.63, sellDate: '2025-11-15', sellTime: '16:14', inStock: false },
{ model: 'Windbreaker Jacket', price: 1161.43, sellDate: '2025-06-24', sellTime: '13:19', inStock: false },
{ model: 'LED Bike Light', price: 1012.5, sellDate: '2025-05-01', sellTime: '17:30', inStock: false },
{ model: 'Windbreaker Jacket', price: 635.37, sellDate: '2025-05-14', sellTime: '09:05', inStock: true },
{ model: 'Road Tire Tube', price: 1421.27, sellDate: '2025-01-31', sellTime: '13:33', inStock: true },
{ model: 'Action Camera', price: 1019.05, sellDate: '2025-12-07', sellTime: '01:26', inStock: false },
{ model: 'Carbon Handlebar', price: 603.96, sellDate: '2025-09-13', sellTime: '04:10', inStock: false },
];
const container = document.getElementById('example8');
const hot = new Handsontable(container, {
data,
colHeaders: true,
rowHeaders: true,
columns: [
{
title: 'Model',
type: 'text',
data: 'model',
width: 150,
headerClassName: 'htLeft',
},
{
title: 'Price',
type: 'numeric',
data: 'price',
width: 80,
locale: 'en-US',
numericFormat: {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
},
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'Date',
type: 'intl-date',
data: 'sellDate',
width: 131,
locale: 'en-US',
dateFormat: { month: 'short', day: 'numeric', year: 'numeric' },
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'Time',
type: 'intl-time',
data: 'sellTime',
width: 90,
locale: 'en-US',
timeFormat: { hour: '2-digit', minute: '2-digit', hour12: true },
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'In stock',
type: 'checkbox',
data: 'inStock',
className: 'htCenter',
headerClassName: 'htCenter',
},
],
width: '100%',
height: 300,
stretchH: 'all',
dialog: {
content: 'This dialog can be controlled programmatically.',
closable: true,
},
licenseKey: 'non-commercial-and-evaluation',
});
const dialogPlugin = hot.getPlugin('dialog');
// Add event listeners for buttons
document.getElementById('showDialog').addEventListener('click', () => {
dialogPlugin.show({
content: 'Dialog shown programmatically!',
closable: true,
});
});
document.getElementById('hideDialog').addEventListener('click', () => {
dialogPlugin.hide();
});
TypeScript
import Handsontable from 'handsontable/base';
import { registerAllModules } from 'handsontable/registry';
// register Handsontable's modules
registerAllModules();
const data = [
{ model: 'Trail Helmet', price: 1298.14, sellDate: '2025-08-31', sellTime: '14:12', inStock: true },
{ model: 'Windbreaker Jacket', price: 178.9, sellDate: '2025-05-10', sellTime: '22:26', inStock: false },
{ model: 'Cycling Cap', price: 288.1, sellDate: '2025-09-15', sellTime: '09:37', inStock: true },
{ model: 'HL Mountain Frame', price: 94.49, sellDate: '2025-01-17', sellTime: '14:19', inStock: false },
{ model: 'Racing Socks', price: 430.38, sellDate: '2025-05-10', sellTime: '13:42', inStock: true },
{ model: 'Racing Socks', price: 138.85, sellDate: '2025-09-20', sellTime: '14:48', inStock: true },
{ model: 'HL Mountain Frame', price: 1909.63, sellDate: '2025-09-05', sellTime: '09:35', inStock: false },
{ model: 'Carbon Handlebar', price: 1080.7, sellDate: '2025-10-24', sellTime: '22:58', inStock: false },
{ model: 'Aero Bottle', price: 1571.13, sellDate: '2025-05-24', sellTime: '00:24', inStock: true },
{ model: 'Windbreaker Jacket', price: 919.09, sellDate: '2025-07-16', sellTime: '19:11', inStock: true },
{ model: 'HL Road Tire', price: 886.22, sellDate: '2025-09-09', sellTime: '00:42', inStock: false },
{ model: 'Speed Gloves', price: 635.13, sellDate: '2025-11-17', sellTime: '12:45', inStock: true },
{ model: 'Trail Helmet', price: 1440.64, sellDate: '2025-01-03', sellTime: '20:16', inStock: false },
{ model: 'Aero Bottle', price: 944.63, sellDate: '2025-11-15', sellTime: '16:14', inStock: false },
{ model: 'Windbreaker Jacket', price: 1161.43, sellDate: '2025-06-24', sellTime: '13:19', inStock: false },
{ model: 'LED Bike Light', price: 1012.5, sellDate: '2025-05-01', sellTime: '17:30', inStock: false },
{ model: 'Windbreaker Jacket', price: 635.37, sellDate: '2025-05-14', sellTime: '09:05', inStock: true },
{ model: 'Road Tire Tube', price: 1421.27, sellDate: '2025-01-31', sellTime: '13:33', inStock: true },
{ model: 'Action Camera', price: 1019.05, sellDate: '2025-12-07', sellTime: '01:26', inStock: false },
{ model: 'Carbon Handlebar', price: 603.96, sellDate: '2025-09-13', sellTime: '04:10', inStock: false },
];
const container = document.getElementById('example8')!;
const hot = new Handsontable(container, {
data,
colHeaders: true,
rowHeaders: true,
columns: [
{
title: 'Model',
type: 'text',
data: 'model',
width: 150,
headerClassName: 'htLeft',
},
{
title: 'Price',
type: 'numeric',
data: 'price',
width: 80,
locale: 'en-US',
numericFormat: {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
},
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'Date',
type: 'intl-date',
data: 'sellDate',
width: 131,
locale: 'en-US',
dateFormat: { month: 'short', day: 'numeric', year: 'numeric' },
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'Time',
type: 'intl-time',
data: 'sellTime',
width: 90,
locale: 'en-US',
timeFormat: { hour: '2-digit', minute: '2-digit', hour12: true },
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'In stock',
type: 'checkbox',
data: 'inStock',
className: 'htCenter',
headerClassName: 'htCenter',
},
],
width: '100%',
height: 300,
stretchH: 'all',
dialog: {
content: 'This dialog can be controlled programmatically.',
closable: true,
},
licenseKey: 'non-commercial-and-evaluation',
});
const dialogPlugin = hot.getPlugin('dialog');
// Add event listeners for buttons
(document.getElementById('showDialog') as HTMLElement).addEventListener('click', () => {
dialogPlugin.show({
content: 'Dialog shown programmatically!',
closable: true,
});
});
(document.getElementById('hideDialog') as HTMLElement).addEventListener('click', () => {
dialogPlugin.hide();
});
HTML
<div class="example-controls-container">
<div class="controls">
<button type="button" id="showDialog">Show Dialog</button>
<button type="button" id="hideDialog">Hide Dialog</button>
</div>
</div>
<div id="example8"></div>

Configuration options

Plugins

Hooks