JavaScript Data GridLoading
Display loading indicators and progress feedback in your data grid application using the Loading plugin.
Overview
The Loading plugin provides a loading overlay for Handsontable using the Dialog plugin. It displays a loading indicator with a customizable title, icon, and description. This is useful for showing progress feedback during data operations, API calls, or any other time-consuming tasks.
With simplicity and effectiveness in mind, the loading plugin was designed to provide a consistent user experience with customizable appearance and behavior. It requires the Dialog plugin to be enabled to function properly.
Basic configuration
To enable the Loading plugin, set the loading option to true or provide a configuration object.
import Handsontable from 'handsontable/base';
import { registerAllModules } from 'handsontable/registry';
import 'handsontable/styles/handsontable.css';
import 'handsontable/styles/ht-theme-main.css';
// register Handsontable's modules
registerAllModules();
const data = [
{ model: 'Trail Helmet', price: 1298.14, sellDate: 'Aug 31, 2025', sellTime: '02:12 PM', inStock: true },
{ model: 'Windbreaker Jacket', price: 178.9, sellDate: 'May 10, 2025', sellTime: '10:26 PM', inStock: false },
{ model: 'Cycling Cap', price: 288.1, sellDate: 'Sep 15, 2025', sellTime: '09:37 AM', inStock: true },
{ model: 'HL Mountain Frame', price: 94.49, sellDate: 'Jan 17, 2025', sellTime: '02:19 PM', inStock: false },
{ model: 'Racing Socks', price: 430.38, sellDate: 'May 10, 2025', sellTime: '01:42 PM', inStock: true },
{ model: 'Racing Socks', price: 138.85, sellDate: 'Sep 20, 2025', sellTime: '02:48 PM', inStock: true },
{ model: 'HL Mountain Frame', price: 1909.63, sellDate: 'Sep 5, 2025', sellTime: '09:35 AM', inStock: false },
{ model: 'Carbon Handlebar', price: 1080.7, sellDate: 'Oct 24, 2025', sellTime: '10:58 PM', inStock: false },
{ model: 'Aero Bottle', price: 1571.13, sellDate: 'May 24, 2025', sellTime: '12:24 AM', inStock: true },
{ model: 'Windbreaker Jacket', price: 919.09, sellDate: 'Jul 16, 2025', sellTime: '07:11 PM', inStock: true },
{ model: 'HL Road Tire', price: 886.22, sellDate: 'Sep 9, 2025', sellTime: '12:42 AM', inStock: false },
{ model: 'Speed Gloves', price: 635.13, sellDate: 'Nov 17, 2025', sellTime: '12:45 PM', inStock: true },
{ model: 'Trail Helmet', price: 1440.64, sellDate: 'Jan 3, 2025', sellTime: '08:16 PM', inStock: false },
{ model: 'Aero Bottle', price: 944.63, sellDate: 'Nov 15, 2025', sellTime: '04:14 PM', inStock: false },
{ model: 'Windbreaker Jacket', price: 1161.43, sellDate: 'Jun 24, 2025', sellTime: '01:19 PM', inStock: false },
{ model: 'LED Bike Light', price: 1012.5, sellDate: 'May 1, 2025', sellTime: '05:30 PM', inStock: false },
{ model: 'Windbreaker Jacket', price: 635.37, sellDate: 'May 14, 2025', sellTime: '09:05 AM', inStock: true },
{ model: 'Road Tire Tube', price: 1421.27, sellDate: 'Jan 31, 2025', sellTime: '01:33 PM', inStock: true },
{ model: 'Action Camera', price: 1019.05, sellDate: 'Dec 7, 2025', sellTime: '01:26 AM', inStock: false },
{ model: 'Carbon Handlebar', price: 603.96, sellDate: 'Sep 13, 2025', sellTime: '04:10 AM', inStock: false },
];
const container = document.getElementById('example1');
const hot = new Handsontable(container, {
themeName: 'ht-theme-main',
data,
colHeaders: true,
rowHeaders: true,
columns: [
{
title: 'Model',
type: 'text',
data: 'model',
width: 150,
headerClassName: 'htLeft',
},
{
title: 'Price',
type: 'numeric',
data: 'price',
width: 80,
numericFormat: {
pattern: '$0,0.00',
culture: 'en-US',
},
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'Date',
type: 'date',
data: 'sellDate',
width: 130,
dateFormat: 'MMM D, YYYY',
correctFormat: true,
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'Time',
type: 'time',
data: 'sellTime',
width: 90,
timeFormat: 'hh:mm A',
correctFormat: true,
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'In stock',
type: 'checkbox',
data: 'inStock',
className: 'htCenter',
headerClassName: 'htCenter',
},
],
width: '100%',
height: 300,
stretchH: 'all',
loading: true,
licenseKey: 'non-commercial-and-evaluation',
});
// Show loading dialog after initialization
const loadingPlugin = hot.getPlugin('loading');
loadingPlugin.show(); Custom configuration
The loading dialog supports customization of the icon, title, and description.
import Handsontable from 'handsontable/base';
import { registerAllModules } from 'handsontable/registry';
import 'handsontable/styles/handsontable.css';
import 'handsontable/styles/ht-theme-main.css';
// register Handsontable's modules
registerAllModules();
const data = [
{ model: 'Trail Helmet', price: 1298.14, sellDate: 'Aug 31, 2025', sellTime: '02:12 PM', inStock: true },
{ model: 'Windbreaker Jacket', price: 178.9, sellDate: 'May 10, 2025', sellTime: '10:26 PM', inStock: false },
{ model: 'Cycling Cap', price: 288.1, sellDate: 'Sep 15, 2025', sellTime: '09:37 AM', inStock: true },
{ model: 'HL Mountain Frame', price: 94.49, sellDate: 'Jan 17, 2025', sellTime: '02:19 PM', inStock: false },
{ model: 'Racing Socks', price: 430.38, sellDate: 'May 10, 2025', sellTime: '01:42 PM', inStock: true },
{ model: 'Racing Socks', price: 138.85, sellDate: 'Sep 20, 2025', sellTime: '02:48 PM', inStock: true },
{ model: 'HL Mountain Frame', price: 1909.63, sellDate: 'Sep 5, 2025', sellTime: '09:35 AM', inStock: false },
{ model: 'Carbon Handlebar', price: 1080.7, sellDate: 'Oct 24, 2025', sellTime: '10:58 PM', inStock: false },
{ model: 'Aero Bottle', price: 1571.13, sellDate: 'May 24, 2025', sellTime: '12:24 AM', inStock: true },
{ model: 'Windbreaker Jacket', price: 919.09, sellDate: 'Jul 16, 2025', sellTime: '07:11 PM', inStock: true },
{ model: 'HL Road Tire', price: 886.22, sellDate: 'Sep 9, 2025', sellTime: '12:42 AM', inStock: false },
{ model: 'Speed Gloves', price: 635.13, sellDate: 'Nov 17, 2025', sellTime: '12:45 PM', inStock: true },
{ model: 'Trail Helmet', price: 1440.64, sellDate: 'Jan 3, 2025', sellTime: '08:16 PM', inStock: false },
{ model: 'Aero Bottle', price: 944.63, sellDate: 'Nov 15, 2025', sellTime: '04:14 PM', inStock: false },
{ model: 'Windbreaker Jacket', price: 1161.43, sellDate: 'Jun 24, 2025', sellTime: '01:19 PM', inStock: false },
{ model: 'LED Bike Light', price: 1012.5, sellDate: 'May 1, 2025', sellTime: '05:30 PM', inStock: false },
{ model: 'Windbreaker Jacket', price: 635.37, sellDate: 'May 14, 2025', sellTime: '09:05 AM', inStock: true },
{ model: 'Road Tire Tube', price: 1421.27, sellDate: 'Jan 31, 2025', sellTime: '01:33 PM', inStock: true },
{ model: 'Action Camera', price: 1019.05, sellDate: 'Dec 7, 2025', sellTime: '01:26 AM', inStock: false },
{ model: 'Carbon Handlebar', price: 603.96, sellDate: 'Sep 13, 2025', sellTime: '04:10 AM', inStock: false },
];
const container = document.getElementById('example2');
const hot = new Handsontable(container, {
themeName: 'ht-theme-main',
data,
colHeaders: true,
rowHeaders: true,
columns: [
{
title: 'Model',
type: 'text',
data: 'model',
width: 150,
headerClassName: 'htLeft',
},
{
title: 'Price',
type: 'numeric',
data: 'price',
width: 80,
numericFormat: {
pattern: '$0,0.00',
culture: 'en-US',
},
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'Date',
type: 'date',
data: 'sellDate',
width: 130,
dateFormat: 'MMM D, YYYY',
correctFormat: true,
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'Time',
type: 'time',
data: 'sellTime',
width: 90,
timeFormat: 'hh:mm A',
correctFormat: true,
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'In stock',
type: 'checkbox',
data: 'inStock',
className: 'htCenter',
headerClassName: 'htCenter',
},
],
width: '100%',
height: 300,
stretchH: 'all',
loading: {
icon: '<svg class="ht-loading__icon-svg" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 16"><path stroke="currentColor" stroke-width="2" d="M15 8a7 7 0 1 1-3.5-6.062"></path></svg>',
title: 'Processing Data...',
description: 'Please wait while we load your inventory data...',
},
licenseKey: 'non-commercial-and-evaluation',
});
// Show loading dialog after initialization
const loadingPlugin = hot.getPlugin('loading');
loadingPlugin.show(); Real-world usage
Here are some common scenarios where the loading dialog is useful:
import Handsontable from 'handsontable/base';
import { registerAllModules } from 'handsontable/registry';
import 'handsontable/styles/handsontable.css';
import 'handsontable/styles/ht-theme-main.css';
// register Handsontable's modules
registerAllModules();
const container = document.getElementById('example3');
const hot = new Handsontable(container, {
themeName: 'ht-theme-main',
data: [],
colHeaders: true,
rowHeaders: true,
columns: [
{
title: 'Model',
type: 'text',
data: 'model',
width: 150,
headerClassName: 'htLeft',
},
{
title: 'Price',
type: 'numeric',
data: 'price',
width: 80,
numericFormat: {
pattern: '$0,0.00',
culture: 'en-US',
},
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'Date',
type: 'date',
data: 'sellDate',
width: 130,
dateFormat: 'MMM D, YYYY',
correctFormat: true,
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'Time',
type: 'time',
data: 'sellTime',
width: 90,
timeFormat: 'hh:mm A',
correctFormat: true,
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'In stock',
type: 'checkbox',
data: 'inStock',
className: 'htCenter',
headerClassName: 'htCenter',
},
],
width: '100%',
height: 300,
stretchH: 'all',
loading: true,
licenseKey: 'non-commercial-and-evaluation',
});
// Get loading plugin instance
const loadingPlugin = hot.getPlugin('loading');
const loadDataButton = document.getElementById('example3-loadData');
// Simulate data loading
async function loadData() {
// Show loading dialog
loadingPlugin.show();
loadDataButton.disabled = true;
try {
// Simulate API call delay
await new Promise((resolve) => setTimeout(resolve, 3000));
// Simulated data
const data = [
{ model: 'Trail Helmet', price: 1298.14, sellDate: 'Aug 31, 2025', sellTime: '02:12 PM', inStock: true },
{ model: 'Windbreaker Jacket', price: 178.9, sellDate: 'May 10, 2025', sellTime: '10:26 PM', inStock: false },
{ model: 'Cycling Cap', price: 288.1, sellDate: 'Sep 15, 2025', sellTime: '09:37 AM', inStock: true },
{ model: 'HL Mountain Frame', price: 94.49, sellDate: 'Jan 17, 2025', sellTime: '02:19 PM', inStock: false },
{ model: 'Racing Socks', price: 430.38, sellDate: 'May 10, 2025', sellTime: '01:42 PM', inStock: true },
{ model: 'Racing Socks', price: 138.85, sellDate: 'Sep 20, 2025', sellTime: '02:48 PM', inStock: true },
{ model: 'HL Mountain Frame', price: 1909.63, sellDate: 'Sep 5, 2025', sellTime: '09:35 AM', inStock: false },
{ model: 'Carbon Handlebar', price: 1080.7, sellDate: 'Oct 24, 2025', sellTime: '10:58 PM', inStock: false },
{ model: 'Aero Bottle', price: 1571.13, sellDate: 'May 24, 2025', sellTime: '12:24 AM', inStock: true },
{ model: 'Windbreaker Jacket', price: 919.09, sellDate: 'Jul 16, 2025', sellTime: '07:11 PM', inStock: true },
];
// Load data into the table
hot.loadData(data);
// Hide loading dialog
loadingPlugin.hide();
loadDataButton.disabled = false;
loadDataButton.innerHTML = 'Reload Data';
} catch (error) {
// Handle error
setTimeout(() => {
loadingPlugin.hide();
loadDataButton.disabled = false;
loadDataButton.innerHTML = 'Load Data';
}, 2000);
}
}
loadDataButton.addEventListener('click', loadData); <div style="margin-bottom: 16px; display: flex; gap: 10px">
<button id="example3-loadData">Load Data</button>
</div>
<div id="example3"></div> Loading with Pagination plugin
The example below demonstrates how to use the Loading plugin with pagination in external container:
This is a demonstration of how to use the Loading plugin with pagination in external container. You need to create pagination overlay manually, after that you can use the afterLoadingShow and afterLoadingHide hooks to show and hide the pagination container overlay.
import Handsontable from 'handsontable/base';
import { registerAllModules } from 'handsontable/registry';
import 'handsontable/styles/handsontable.css';
import 'handsontable/styles/ht-theme-main.css';
// register Handsontable's modules
registerAllModules();
const container = document.getElementById('example4');
const paginationContainer = document.getElementById('example4-pagination');
const hot = new Handsontable(container, {
themeName: 'ht-theme-main',
data: [],
colHeaders: true,
rowHeaders: true,
columns: [
{
title: 'Model',
type: 'text',
data: 'model',
width: 150,
headerClassName: 'htLeft',
},
{
title: 'Price',
type: 'numeric',
data: 'price',
width: 80,
numericFormat: {
pattern: '$0,0.00',
culture: 'en-US',
},
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'Date',
type: 'date',
data: 'sellDate',
width: 130,
dateFormat: 'MMM D, YYYY',
correctFormat: true,
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'Time',
type: 'time',
data: 'sellTime',
width: 90,
timeFormat: 'hh:mm A',
correctFormat: true,
className: 'htRight',
headerClassName: 'htRight',
},
{
title: 'In stock',
type: 'checkbox',
data: 'inStock',
className: 'htCenter',
headerClassName: 'htCenter',
},
],
width: '100%',
height: 300,
stretchH: 'all',
loading: true,
pagination: {
uiContainer: paginationContainer,
},
licenseKey: 'non-commercial-and-evaluation',
});
// Add hooks to show and hide the pagination container overlay
hot.addHook('afterLoadingShow', () => {
paginationContainer.classList.add('overlay');
});
hot.addHook('afterLoadingHide', () => {
paginationContainer.classList.remove('overlay');
});
// Get loading plugin instance
const loadingPlugin = hot.getPlugin('loading');
const loadDataButton = document.getElementById('example4-loadData');
// Simulate data loading
async function loadData() {
// Show loading dialog
loadingPlugin.show();
loadDataButton.disabled = true;
try {
// Simulate API call delay
await new Promise((resolve) => setTimeout(resolve, 3000));
// Simulated data
const data = [
{ model: 'Trail Helmet', price: 1298.14, sellDate: 'Aug 31, 2025', sellTime: '02:12 PM', inStock: true },
{ model: 'Windbreaker Jacket', price: 178.9, sellDate: 'May 10, 2025', sellTime: '10:26 PM', inStock: false },
{ model: 'Cycling Cap', price: 288.1, sellDate: 'Sep 15, 2025', sellTime: '09:37 AM', inStock: true },
{ model: 'HL Mountain Frame', price: 94.49, sellDate: 'Jan 17, 2025', sellTime: '02:19 PM', inStock: false },
{ model: 'Racing Socks', price: 430.38, sellDate: 'May 10, 2025', sellTime: '01:42 PM', inStock: true },
{ model: 'Racing Socks', price: 138.85, sellDate: 'Sep 20, 2025', sellTime: '02:48 PM', inStock: true },
{ model: 'HL Mountain Frame', price: 1909.63, sellDate: 'Sep 5, 2025', sellTime: '09:35 AM', inStock: false },
{ model: 'Carbon Handlebar', price: 1080.7, sellDate: 'Oct 24, 2025', sellTime: '10:58 PM', inStock: false },
{ model: 'Aero Bottle', price: 1571.13, sellDate: 'May 24, 2025', sellTime: '12:24 AM', inStock: true },
{ model: 'Windbreaker Jacket', price: 919.09, sellDate: 'Jul 16, 2025', sellTime: '07:11 PM', inStock: true },
{ model: 'LED Bike Light', price: 1012.5, sellDate: 'May 1, 2025', sellTime: '05:30 PM', inStock: false },
{ model: 'Windbreaker Jacket', price: 635.37, sellDate: 'May 14, 2025', sellTime: '09:05 AM', inStock: true },
{ model: 'Road Tire Tube', price: 1421.27, sellDate: 'Jan 31, 2025', sellTime: '01:33 PM', inStock: true },
{ model: 'Action Camera', price: 1019.05, sellDate: 'Dec 7, 2025', sellTime: '01:26 AM', inStock: false },
{ model: 'Carbon Handlebar', price: 603.96, sellDate: 'Sep 13, 2025', sellTime: '04:10 AM', inStock: false },
{ model: 'Aero Bottle', price: 1334.03, sellDate: 'Jan 24, 2025', sellTime: '03:29 AM', inStock: false },
{ model: 'Road Tire Tube', price: 1841.17, sellDate: 'May 22, 2025', sellTime: '01:45 AM', inStock: false },
{ model: 'Aero Bottle', price: 1622.05, sellDate: 'Jan 13, 2025', sellTime: '08:30 AM', inStock: true },
{ model: 'Comfort Saddle', price: 1456.24, sellDate: 'Jul 20, 2025', sellTime: '03:39 AM', inStock: false },
{ model: 'Windbreaker Jacket', price: 1736.96, sellDate: 'Sep 25, 2025', sellTime: '12:43 AM', inStock: true },
{ model: 'Fitness Watch', price: 1075.31, sellDate: 'Nov 7, 2025', sellTime: '05:47 PM', inStock: true },
{ model: 'Cycling Cap', price: 726.01, sellDate: 'Oct 28, 2025', sellTime: '12:44 PM', inStock: true },
{ model: 'Road Tire Tube', price: 601.99, sellDate: 'Sep 22, 2025', sellTime: '12:26 AM', inStock: true },
{ model: 'Speed Gloves', price: 1758.26, sellDate: 'Oct 4, 2025', sellTime: '04:59 AM', inStock: true },
{ model: 'Speed Gloves', price: 564.35, sellDate: 'Jul 10, 2025', sellTime: '06:21 PM', inStock: true },
{ model: 'Hydration Pack', price: 954.84, sellDate: 'Nov 2, 2025', sellTime: '12:59 AM', inStock: false },
{ model: 'Cycling Cap', price: 1511.5, sellDate: 'Feb 11, 2025', sellTime: '02:38 AM', inStock: false },
{ model: 'HL Road Tire', price: 269.6, sellDate: 'Jun 18, 2025', sellTime: '04:58 AM', inStock: false },
];
// Load data into the table
hot.loadData(data);
// Hide loading dialog
loadingPlugin.hide();
loadDataButton.disabled = false;
loadDataButton.innerHTML = 'Reload Data';
} catch (error) {
// Handle error
setTimeout(() => {
loadingPlugin.hide();
loadDataButton.disabled = false;
loadDataButton.innerHTML = 'Load Data';
}, 2000);
}
}
loadDataButton.addEventListener('click', loadData); <div id="example4"></div>
<div style="margin-top: 16px; display: flex; gap: 10px">
<button id="example4-loadData">Load Data</button>
</div>
<div style="margin-top: 16px;">
<p style="padding: 0;">This is a demonstration of how to use the Loading plugin with pagination in external container. You need to create pagination overlay manually, after that you can use the <code>afterLoadingShow</code> and <code>afterLoadingHide</code> hooks to show and hide the pagination container overlay.</p>
</div>
<div style="margin-top: 16px;">
<div id="example4-pagination"></div>
</div> #example4-pagination {
position: relative;
}
#example4-pagination.overlay .ht-pagination::before {
content: "";
display: block;
width: 100%;
height: 100%;
background-color: var(--ht-dialog-semi-transparent-background-color);
position: absolute;
inset: 0;
z-index: 1000;
} Localize loading
Translate default loading dialog labels using the global translations mechanism. The loading dialog introduces the following keys to the language dictionary that you can use to translate the loading UI:
LOADING_TITLE = 'Loading...'
To learn more about the translation mechanism, see the Languages guide.
Related API reference
There is a newer version of Handsontable available. Switch to the latest version ⟶