Skip to content

Use Handsontable’s built-in themes or customize its look using the Theme API or CSS variables.

Overview

Handsontable themes manage most visual elements of the data grid. Three built-in themes are available: main, horizon, and classic. All themes include dark and light modes.

The recommended way to apply themes is using the Theme API, which allows you to register and configure themes programmatically with runtime features like density modes and color schemes. Alternatively, you can use CSS files and pass the theme name as a string for a simpler setup.

Built-in themes

The main theme offers a spreadsheet-like interface, perfect for batch-editing tasks and providing users with a familiar experience, similar to other popular spreadsheet software on the market.

The horizon theme, on the other hand, is better suited for data display and analysis. It hides the vertical lines between columns, giving it a cleaner and more lightweight feel.

The classic theme is a replacement for the old legacy style. It retains the familiar look and feel of the original legacy styles, but has been updated to allow customization with CSS variables. This theme is ideal for users who prefer the traditional appearance of Handsontable but want to benefit from the theming system. The classic theme supports both light and dark modes, ensuring a seamless integration with your application’s color scheme preferences.

Keep in mind that starting from version 15.0, importing a theme is required.

JavaScript
import Handsontable from 'handsontable';
import { mainTheme, horizonTheme, classicTheme, registerTheme, getTheme } from 'handsontable/themes';
registerTheme(mainTheme);
registerTheme(horizonTheme);
registerTheme(classicTheme);
// constants.js
102 collapsed lines
export const data = [
[false, 'Tagcat', 'United Kingdom', 'Classic Vest', '2025-10-11', '01-2331942', true, '172', 2, 2],
[true, 'Zoomzone', 'Indonesia', 'Cycling Cap', '2025-05-03', '88-2768633', true, '188', 6, 2],
[true, 'Meeveo', 'United States', 'Full-Finger Gloves', '2025-03-27', '51-6775945', true, '162', 1, 3],
[false, 'Buzzdog', 'Philippines', 'HL Mountain Frame', '2025-08-29', '44-4028109', true, '133', 7, 1],
[true, 'Katz', 'India', 'Half-Finger Gloves', '2025-10-02', '08-2758492', true, '87', 1, 3],
[false, 'Jaxbean', 'China', 'HL Road Frame', '2025-09-28', '84-3557705', false, '26', 8, 1],
[false, 'Wikido', 'Brazil', 'HL Touring Frame', '2025-06-24', '20-9397637', false, '110', 4, 1],
[false, 'Browsedrive', 'United States', 'LL Mountain Frame', '2025-03-13', '36-0079556', true, '50', 4, 4],
[false, 'Twinder', 'United Kingdom', 'LL Road Frame', '2025-04-06', '41-1489542', false, '160', 6, 1],
[false, 'Jetwire', 'China', 'LL Touring Frame', '2025-02-01', '37-1531629', true, '30', 8, 5],
[false, 'Chatterpoint', 'China', 'Long-Sleeve Logo Jersey', '2025-07-14', '25-5083429', true, '39', 7, 2],
[false, 'Twinder', 'Egypt', "Men's Bib-Shorts", '2025-08-31', '04-4281278', false, '96', 6, 1],
[false, 'Midel', 'United States', "Men's Sports Shorts", '2025-06-27', '55-1711908', true, '108', 10, 3],
[false, 'Yodo', 'India', 'ML Mountain Frame', '2025-03-16', '58-8360815', false, '46', 1, 1],
[false, 'Camido', 'Russia', 'ML Mountain Frame-W', '2025-09-13', '10-3786104', true, '97', 8, 3],
[false, 'Eire', 'Thailand', 'ML Road Frame', '2025-04-10', '45-1186054', true, '161', 1, 4],
[false, 'Vinte', 'United Kingdom', 'ML Road Frame-W', '2025-01-22', '62-6202742', true, '58', 4, 3],
[false, 'Twitterlist', 'China', 'Mountain Bike Socks', '2025-11-09', '88-9646223', true, '92', 8, 3],
[false, 'Eidel', 'Bangladesh', 'Mountain-100', '2025-09-19', '45-5588112', true, '5', 6, 5],
[false, 'Trunyx', 'Nigeria', 'Mountain-200', '2025-03-09', '66-6271819', true, '158', 4, 1],
[false, 'Katz', 'Turkey', 'Mountain-300', '2025-03-05', '38-9245023', false, '121', 5, 4],
[false, 'Kaymbo', 'United States', 'Mountain-400-W', '2025-12-24', '44-5916927', false, '61', 5, 4],
[false, 'Ozu', 'Pakistan', 'Mountain-500', '2025-06-13', '31-5449914', true, '155', 2, 2],
[false, 'Rhynyx', 'India', 'Racing Socks', '2025-12-05', '19-9413869', true, '162', 2, 4],
[false, 'Flashset', 'Iran', 'Road-150', '2025-12-14', '25-9807605', false, '46', 7, 1],
[false, 'Yata', 'Congo (Kinshasa)', 'Road-250', '2025-06-12', '74-4291983', true, '47', 4, 4],
[false, 'Brainlounge', 'Vietnam', 'Road-350-W', '2025-03-10', '83-0980643', true, '104', 2, 3],
[false, 'Babblestorm', 'United States', 'Road-450', '2025-10-10', '19-2878430', true, '101', 6, 4],
[false, 'Youspan', 'Brazil', 'Road-550-W', '2025-12-16', '19-1838230', true, '150', 10, 3],
[false, 'Nlounge', 'China', 'Road-650', '2025-10-31', '32-2267938', true, '42', 4, 2],
[false, 'Twinte', 'India', 'Road-750', '2025-08-17', '79-2821972', true, '144', 9, 3],
[false, 'Oyonder', 'United Kingdom', 'Short-Sleeve Classic Jersey', '2025-12-04', '46-6597557', true, '195', 4, 1],
[false, 'Gigabox', 'Pakistan', 'Sport-100', '2025-02-03', '15-1793960', true, '199', 4, 4],
[false, 'Livetube', 'France', 'Touring-1000', '2025-05-16', '86-0811003', true, '110', 4, 5],
[false, 'Voomm', 'United Kingdom', 'Touring-2000', '2025-07-15', '95-3068680', true, '51', 4, 4],
[false, 'Voonyx', 'China', 'Touring-3000', '2025-11-27', '35-3085360', false, '69', 2, 5],
[false, 'Zoombeat', 'United States', "Women's Mountain Shorts", '2025-11-03', '56-8673088', false, '53', 2, 3],
[false, 'Roomm', 'China', "Women's Tights", '2025-03-16', '76-0085918', true, '168', 1, 1],
[false, 'Leenti', 'China', 'Mountain-400', '2025-05-16', '03-0893276', false, '58', 1, 4],
[false, 'Jetpulse', 'United States', 'Road-550', '2025-02-08', '79-9013306', true, '152', 9, 3],
[false, 'Katz', 'Peru', 'Road-350', '2025-02-15', '55-7799920', true, '66', 4, 2],
[false, 'Cogidoo', 'India', 'LL Mountain Front Wheel', '2025-06-04', '07-0881122', false, '112', 9, 2],
[false, 'Divavu', 'Colombia', 'Touring Rear Wheel', '2025-02-24', '58-6157387', true, '50', 10, 4],
[false, 'Mydeo', 'China', 'Touring Front Wheel', '2025-12-07', '12-2810010', false, '31', 3, 5],
[false, 'Browsebug', 'Japan', 'ML Mountain Front Wheel', '2025-01-14', '64-9249984', true, '132', 5, 5],
[false, 'Layo', 'China', 'HL Mountain Front Wheel', '2025-04-24', '45-0739652', true, '45', 1, 5],
[false, 'Snaptags', 'United Kingdom', 'LL Touring Handlebars', '2025-08-06', '09-5712761', true, '197', 4, 2],
[false, 'Cogilith', 'China', 'HL Touring Handlebars', '2025-05-31', '01-7345008', true, '190', 4, 3],
[false, 'Reallinks', 'United Kingdom', 'LL Road Front Wheel', '2025-05-14', '62-1065350', true, '184', 3, 4],
[false, 'Quaxo', 'United States', 'ML Road Front Wheel', '2025-03-23', '44-7241323', true, '169', 3, 4],
[false, 'Devify', 'China', 'HL Road Front Wheel', '2025-12-12', '52-0295699', false, '152', 4, 4],
[false, 'Youopia', 'Angola', 'LL Mountain Handlebars', '2025-04-01', '52-2650922', false, '182', 6, 4],
[false, 'Ainyx', 'China', 'Touring Pedal', '2025-02-27', '48-3618525', true, '141', 6, 1],
[false, 'Browsetype', 'Malaysia', 'ML Mountain Handlebars', '2025-04-28', '51-8893923', true, '169', 7, 1],
[false, 'Muxo', 'China', 'HL Mountain Handlebars', '2025-08-22', '68-5911361', false, '39', 7, 1],
[false, 'Bubbletube', 'China', 'LL Road Handlebars', '2025-10-04', '41-5880042', true, '71', 8, 3],
[false, 'Fadeo', 'Vietnam', 'ML Road Handlebars', '2025-04-23', '90-5913983', true, '148', 10, 3],
[false, 'Yadel', 'United Kingdom', 'HL Road Handlebars', '2025-04-18', '92-0960699', true, '116', 8, 1],
[false, 'Blognation', 'China', 'LL Headset', '2025-01-10', '06-9493898', true, '96', 10, 1],
[false, 'Devpoint', 'China', 'ML Headset', '2025-12-25', '69-5878565', true, '35', 4, 2],
[false, 'Aibox', 'United Kingdom', 'HL Headset', '2025-03-18', '13-1133017', true, '16', 8, 2],
[false, 'Brightdog', 'China', 'LL Mountain Pedal', '2025-09-11', '39-6530433', true, '194', 2, 5],
[false, 'Gabcube', 'Nigeria', 'ML Mountain Pedal', '2025-04-22', '96-6860388', true, '24', 1, 3],
[false, 'Muxo', 'China', 'HL Mountain Pedal', '2025-06-05', '30-0356137', true, '170', 4, 4],
[false, 'Tambee', 'China', 'ML Touring Seat/Saddle', '2025-02-22', '93-9058255', true, '184', 9, 5],
[false, 'Cogilith', 'India', 'LL Touring Seat/Saddle', '2025-04-06', '82-9268909', false, '153', 10, 4],
[false, 'Dynabox', 'Hong Kong', 'HL Touring Seat/Saddle', '2025-01-10', '20-6913815', false, '88', 10, 1],
[false, 'Shuffledrive', 'Sudan', 'LL Road Pedal', '2025-09-16', '08-8238817', true, '57', 9, 2],
[false, 'Fivechat', 'China', 'ML Road Pedal', '2025-08-26', '44-7370350', false, '62', 4, 1],
[false, 'Meembee', 'United States', 'HL Road Pedal', '2025-12-27', '01-3525949', true, '123', 2, 4],
[false, 'Dynazzy', 'United Kingdom', 'LL Mountain Seat/Saddle 1', '2025-12-15', '04-2414623', true, '77', 10, 5],
[false, 'Eare', 'China', 'ML Mountain Seat/Saddle 1', '2025-04-04', '15-1917509', false, '199', 9, 4],
[false, 'Yozio', 'China', 'HL Mountain Seat/Saddle 1', '2025-03-15', '06-2526845', true, '149', 8, 2],
[false, 'Quinu', "Xi'an", '425-777-7829', '2025-02-22', '83-1713558', false, '191', 9, 5],
[false, 'Jazzy', 'United Kingdom', 'ML Road Seat/Saddle 1', '2025-08-07', '00-8892524', true, '150', 10, 2],
[false, 'Thoughtsphere', 'China', 'HL Road Seat/Saddle 1', '2025-11-28', '39-5538991', true, '130', 7, 3],
[false, 'Leenti', 'China', 'ML Road Rear Wheel', '2025-12-29', '06-9002973', true, '179', 1, 2],
[false, 'Quaxo', 'United Kingdom', 'HL Road Rear Wheel', '2025-09-06', '73-6104901', true, '98', 5, 3],
[false, 'Tanoodle', 'Chile', 'LL Mountain Seat/Saddle 2', '2025-05-24', '68-7384479', true, '175', 2, 3],
[false, 'Feednation', 'China', 'ML Mountain Seat/Saddle 2', '2025-11-21', '26-7757763', true, '11', 1, 3],
[false, 'Kayveo', 'China', 'HL Mountain Seat/Saddle 2', '2025-06-21', '07-4873562', false, '184', 7, 4],
[false, 'Meevee', 'Saudi Arabia', 'LL Road Seat/Saddle 1', '2025-11-16', '46-5819554', false, '27', 9, 3],
[false, 'Twitterworks', 'China', 'ML Road Seat/Saddle 2', '2025-04-19', '01-2666826', true, '186', 3, 2],
[false, 'Wikizz', 'Tanzania', 'HL Road Seat/Saddle 2', '2025-03-08', '54-7090503', true, '20', 3, 3],
[false, 'Yoveo', 'United States', 'LL Mountain Tire', '2025-10-14', '78-7658520', false, '153', 2, 1],
[false, 'Yakidoo', 'China', 'ML Mountain Tire', '2025-10-12', '23-9926318', true, '161', 8, 5],
[false, 'Oyope', 'China', 'HL Mountain Tire', '2025-09-20', '20-0179517', true, '98', 10, 5],
[false, 'Skipstorm', 'United States', 'LL Road Tire', '2025-10-01', '02-9543343', true, '30', 7, 3],
[false, 'Minyx', 'United States', 'ML Road Tire', '2025-07-07', '98-3938169', true, '73', 10, 2],
[false, 'Miboo', 'China', 'HL Road Tire', '2025-07-25', '68-5197934', true, '158', 9, 1],
[false, 'Realfire', 'United States', 'Touring Tire', '2025-08-27', '39-8260460', true, '122', 5, 2],
[false, 'Shufflester', 'China', 'Mountain Tire Tube', '2025-06-08', '45-9776170', true, '33', 2, 4],
[false, 'Ntag', 'China', 'Road Tire Tube', '2025-12-06', '45-0858451', true, '107', 6, 2],
[false, 'Jabberbean', 'United States', 'Touring Tire Tube', '2025-04-26', '15-4247305', true, '15', 1, 2],
[false, 'Thoughtblab', 'China', 'LL Bottom Bracket', '2025-05-21', '15-8534931', true, '168', 5, 2],
[false, 'Jabbertype', 'China', 'Classic Vest', '2025-07-25', '23-1251557', true, '135', 4, 2],
[false, 'Buzzshare', 'United Kingdom', 'Cycling Cap', '2025-07-07', '86-5920601', true, '11', 1, 4],
[false, 'Roodel', 'United States', 'Full-Finger Gloves', '2025-01-13', '48-1055459', true, '41', 6, 4],
[false, 'Zoovu', 'China', 'Half-Finger Gloves', '2025-06-03', '12-7842022', true, '144', 6, 1],
[false, 'Photofeed', 'China', 'HL Mountain Frame', '2025-07-14', '94-5088099', true, '106', 1, 4],
];
const example = document.getElementById('exampleTheme');
const isDark = document.documentElement.getAttribute('data-theme') === 'dark';
const hotInstance = new Handsontable(example, {
theme: getTheme('horizon').setColorScheme(isDark ? 'dark' : 'light'),
data,
height: 450,
colWidths: [180, 220, 140, 120, 120, 120, 140],
colHeaders: ['Company Name', 'Name', 'Sell date', 'In stock', 'Quantity', 'Order ID', 'Country'],
contextMenu: [
'cut',
'copy',
'---------',
'row_above',
'row_below',
'remove_row',
'---------',
'alignment',
'make_read_only',
'clear_column',
],
columns: [
{
data: 1,
type: 'text',
headerClassName: 'htLeft',
},
{
data: 3,
type: 'text',
headerClassName: 'htLeft',
},
{
data: 4,
type: 'intl-date',
locale: 'en-GB',
dateFormat: { day: '2-digit', month: '2-digit', year: 'numeric' },
headerClassName: 'htLeft',
},
{
data: 6,
type: 'checkbox',
className: 'htCenter',
headerClassName: 'htLeft',
},
{
data: 7,
type: 'numeric',
headerClassName: 'htLeft',
},
{
data: 5,
type: 'text',
headerClassName: 'htLeft',
},
{
data: 2,
type: 'text',
headerClassName: 'htLeft',
},
],
dropdownMenu: true,
hiddenColumns: {
indicators: true,
},
multiColumnSorting: true,
filters: true,
rowHeaders: true,
manualRowMove: true,
headerClassName: 'htLeft',
autoWrapRow: true,
autoWrapCol: true,
manualRowResize: true,
manualColumnResize: true,
navigableHeaders: true,
licenseKey: 'non-commercial-and-evaluation',
});
// Theme dropdown
const dropdown = document.getElementById('themeDropdown');
const trigger = document.getElementById('themeTrigger');
const triggerLabel = document.getElementById('triggerLabel');
const triggerColors = document.getElementById('triggerColors');
const menu = document.getElementById('themeMenu');
const currentTheme = document.documentElement.getAttribute('data-theme') === 'dark'
? 'horizon-dark'
: 'horizon-light';
const setTheme = (value) => {
const [themeName, colorScheme] = value.split('-');
hotInstance.updateSettings({ theme: getTheme(themeName).setColorScheme(colorScheme || 'auto') });
// Update trigger label
const item = menu.querySelector(`[data-value="${value}"]`);
if (item) {
triggerLabel.textContent = item.textContent.trim();
}
// Update trigger color dots by resolving computed grid styles
const hotRoot = document.querySelector('#exampleTheme .handsontable');
if (hotRoot) {
const helper = document.createElement('div');
helper.style.cssText = 'position:absolute;visibility:hidden;pointer-events:none;';
hotRoot.appendChild(helper);
const resolve = (varName) => {
helper.style.color = `var(${varName})`;
return getComputedStyle(helper).color;
};
const fg = resolve('--ht-foreground-color');
const bg = resolve('--ht-background-color');
const accent = resolve('--ht-accent-color');
hotRoot.removeChild(helper);
const dots = triggerColors.querySelectorAll('.color');
if (dots.length >= 3) {
dots[0].style.background = fg;
dots[1].style.background = bg;
dots[2].style.background = accent;
}
}
// Update aria-selected
menu.querySelectorAll('li').forEach((li) => {
li.setAttribute('aria-selected', li.dataset.value === value ? 'true' : 'false');
});
};
// Toggle dropdown
trigger.addEventListener('click', () => {
const isOpen = !menu.hidden;
menu.hidden = isOpen;
trigger.setAttribute('aria-expanded', String(!isOpen));
});
// Select item
menu.addEventListener('click', (e) => {
const item = e.target.closest('li[data-value]');
if (item) {
setTheme(item.dataset.value);
menu.hidden = true;
trigger.setAttribute('aria-expanded', 'false');
}
});
// Close on outside click
document.addEventListener('click', (e) => {
if (!dropdown.contains(e.target)) {
menu.hidden = true;
trigger.setAttribute('aria-expanded', 'false');
}
});
// Close on Escape
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && !menu.hidden) {
menu.hidden = true;
trigger.setAttribute('aria-expanded', 'false');
trigger.focus();
}
});
// Set initial theme
setTheme(currentTheme);
TypeScript
import Handsontable from 'handsontable';
import { mainTheme, horizonTheme, classicTheme, registerTheme, getTheme } from 'handsontable/themes';
import { PredefinedMenuItemKey } from 'handsontable/plugins/contextMenu';
registerTheme(mainTheme);
registerTheme(horizonTheme);
registerTheme(classicTheme);
103 collapsed lines
// constants.js
export const data: (string | number | boolean)[][] = [
[false, 'Tagcat', 'United Kingdom', 'Classic Vest', '2025-10-11', '01-2331942', true, '172', 2, 2],
[true, 'Zoomzone', 'Indonesia', 'Cycling Cap', '2025-05-03', '88-2768633', true, '188', 6, 2],
[true, 'Meeveo', 'United States', 'Full-Finger Gloves', '2025-03-27', '51-6775945', true, '162', 1, 3],
[false, 'Buzzdog', 'Philippines', 'HL Mountain Frame', '2025-08-29', '44-4028109', true, '133', 7, 1],
[true, 'Katz', 'India', 'Half-Finger Gloves', '2025-10-02', '08-2758492', true, '87', 1, 3],
[false, 'Jaxbean', 'China', 'HL Road Frame', '2025-09-28', '84-3557705', false, '26', 8, 1],
[false, 'Wikido', 'Brazil', 'HL Touring Frame', '2025-06-24', '20-9397637', false, '110', 4, 1],
[false, 'Browsedrive', 'United States', 'LL Mountain Frame', '2025-03-13', '36-0079556', true, '50', 4, 4],
[false, 'Twinder', 'United Kingdom', 'LL Road Frame', '2025-04-06', '41-1489542', false, '160', 6, 1],
[false, 'Jetwire', 'China', 'LL Touring Frame', '2025-02-01', '37-1531629', true, '30', 8, 5],
[false, 'Chatterpoint', 'China', 'Long-Sleeve Logo Jersey', '2025-07-14', '25-5083429', true, '39', 7, 2],
[false, 'Twinder', 'Egypt', "Men's Bib-Shorts", '2025-08-31', '04-4281278', false, '96', 6, 1],
[false, 'Midel', 'United States', "Men's Sports Shorts", '2025-06-27', '55-1711908', true, '108', 10, 3],
[false, 'Yodo', 'India', 'ML Mountain Frame', '2025-03-16', '58-8360815', false, '46', 1, 1],
[false, 'Camido', 'Russia', 'ML Mountain Frame-W', '2025-09-13', '10-3786104', true, '97', 8, 3],
[false, 'Eire', 'Thailand', 'ML Road Frame', '2025-04-10', '45-1186054', true, '161', 1, 4],
[false, 'Vinte', 'United Kingdom', 'ML Road Frame-W', '2025-01-22', '62-6202742', true, '58', 4, 3],
[false, 'Twitterlist', 'China', 'Mountain Bike Socks', '2025-11-09', '88-9646223', true, '92', 8, 3],
[false, 'Eidel', 'Bangladesh', 'Mountain-100', '2025-09-19', '45-5588112', true, '5', 6, 5],
[false, 'Trunyx', 'Nigeria', 'Mountain-200', '2025-03-09', '66-6271819', true, '158', 4, 1],
[false, 'Katz', 'Turkey', 'Mountain-300', '2025-03-05', '38-9245023', false, '121', 5, 4],
[false, 'Kaymbo', 'United States', 'Mountain-400-W', '2025-12-24', '44-5916927', false, '61', 5, 4],
[false, 'Ozu', 'Pakistan', 'Mountain-500', '2025-06-13', '31-5449914', true, '155', 2, 2],
[false, 'Rhynyx', 'India', 'Racing Socks', '2025-12-05', '19-9413869', true, '162', 2, 4],
[false, 'Flashset', 'Iran', 'Road-150', '2025-12-14', '25-9807605', false, '46', 7, 1],
[false, 'Yata', 'Congo (Kinshasa)', 'Road-250', '2025-06-12', '74-4291983', true, '47', 4, 4],
[false, 'Brainlounge', 'Vietnam', 'Road-350-W', '2025-03-10', '83-0980643', true, '104', 2, 3],
[false, 'Babblestorm', 'United States', 'Road-450', '2025-10-10', '19-2878430', true, '101', 6, 4],
[false, 'Youspan', 'Brazil', 'Road-550-W', '2025-12-16', '19-1838230', true, '150', 10, 3],
[false, 'Nlounge', 'China', 'Road-650', '2025-10-31', '32-2267938', true, '42', 4, 2],
[false, 'Twinte', 'India', 'Road-750', '2025-08-17', '79-2821972', true, '144', 9, 3],
[false, 'Oyonder', 'United Kingdom', 'Short-Sleeve Classic Jersey', '2025-12-04', '46-6597557', true, '195', 4, 1],
[false, 'Gigabox', 'Pakistan', 'Sport-100', '2025-02-03', '15-1793960', true, '199', 4, 4],
[false, 'Livetube', 'France', 'Touring-1000', '2025-05-16', '86-0811003', true, '110', 4, 5],
[false, 'Voomm', 'United Kingdom', 'Touring-2000', '2025-07-15', '95-3068680', true, '51', 4, 4],
[false, 'Voonyx', 'China', 'Touring-3000', '2025-11-27', '35-3085360', false, '69', 2, 5],
[false, 'Zoombeat', 'United States', "Women's Mountain Shorts", '2025-11-03', '56-8673088', false, '53', 2, 3],
[false, 'Roomm', 'China', "Women's Tights", '2025-03-16', '76-0085918', true, '168', 1, 1],
[false, 'Leenti', 'China', 'Mountain-400', '2025-05-16', '03-0893276', false, '58', 1, 4],
[false, 'Jetpulse', 'United States', 'Road-550', '2025-02-08', '79-9013306', true, '152', 9, 3],
[false, 'Katz', 'Peru', 'Road-350', '2025-02-15', '55-7799920', true, '66', 4, 2],
[false, 'Cogidoo', 'India', 'LL Mountain Front Wheel', '2025-06-04', '07-0881122', false, '112', 9, 2],
[false, 'Divavu', 'Colombia', 'Touring Rear Wheel', '2025-02-24', '58-6157387', true, '50', 10, 4],
[false, 'Mydeo', 'China', 'Touring Front Wheel', '2025-12-07', '12-2810010', false, '31', 3, 5],
[false, 'Browsebug', 'Japan', 'ML Mountain Front Wheel', '2025-01-14', '64-9249984', true, '132', 5, 5],
[false, 'Layo', 'China', 'HL Mountain Front Wheel', '2025-04-24', '45-0739652', true, '45', 1, 5],
[false, 'Snaptags', 'United Kingdom', 'LL Touring Handlebars', '2025-08-06', '09-5712761', true, '197', 4, 2],
[false, 'Cogilith', 'China', 'HL Touring Handlebars', '2025-05-31', '01-7345008', true, '190', 4, 3],
[false, 'Reallinks', 'United Kingdom', 'LL Road Front Wheel', '2025-05-14', '62-1065350', true, '184', 3, 4],
[false, 'Quaxo', 'United States', 'ML Road Front Wheel', '2025-03-23', '44-7241323', true, '169', 3, 4],
[false, 'Devify', 'China', 'HL Road Front Wheel', '2025-12-12', '52-0295699', false, '152', 4, 4],
[false, 'Youopia', 'Angola', 'LL Mountain Handlebars', '2025-04-01', '52-2650922', false, '182', 6, 4],
[false, 'Ainyx', 'China', 'Touring Pedal', '2025-02-27', '48-3618525', true, '141', 6, 1],
[false, 'Browsetype', 'Malaysia', 'ML Mountain Handlebars', '2025-04-28', '51-8893923', true, '169', 7, 1],
[false, 'Muxo', 'China', 'HL Mountain Handlebars', '2025-08-22', '68-5911361', false, '39', 7, 1],
[false, 'Bubbletube', 'China', 'LL Road Handlebars', '2025-10-04', '41-5880042', true, '71', 8, 3],
[false, 'Fadeo', 'Vietnam', 'ML Road Handlebars', '2025-04-23', '90-5913983', true, '148', 10, 3],
[false, 'Yadel', 'United Kingdom', 'HL Road Handlebars', '2025-04-18', '92-0960699', true, '116', 8, 1],
[false, 'Blognation', 'China', 'LL Headset', '2025-01-10', '06-9493898', true, '96', 10, 1],
[false, 'Devpoint', 'China', 'ML Headset', '2025-12-25', '69-5878565', true, '35', 4, 2],
[false, 'Aibox', 'United Kingdom', 'HL Headset', '2025-03-18', '13-1133017', true, '16', 8, 2],
[false, 'Brightdog', 'China', 'LL Mountain Pedal', '2025-09-11', '39-6530433', true, '194', 2, 5],
[false, 'Gabcube', 'Nigeria', 'ML Mountain Pedal', '2025-04-22', '96-6860388', true, '24', 1, 3],
[false, 'Muxo', 'China', 'HL Mountain Pedal', '2025-06-05', '30-0356137', true, '170', 4, 4],
[false, 'Tambee', 'China', 'ML Touring Seat/Saddle', '2025-02-22', '93-9058255', true, '184', 9, 5],
[false, 'Cogilith', 'India', 'LL Touring Seat/Saddle', '2025-04-06', '82-9268909', false, '153', 10, 4],
[false, 'Dynabox', 'Hong Kong', 'HL Touring Seat/Saddle', '2025-01-10', '20-6913815', false, '88', 10, 1],
[false, 'Shuffledrive', 'Sudan', 'LL Road Pedal', '2025-09-16', '08-8238817', true, '57', 9, 2],
[false, 'Fivechat', 'China', 'ML Road Pedal', '2025-08-26', '44-7370350', false, '62', 4, 1],
[false, 'Meembee', 'United States', 'HL Road Pedal', '2025-12-27', '01-3525949', true, '123', 2, 4],
[false, 'Dynazzy', 'United Kingdom', 'LL Mountain Seat/Saddle 1', '2025-12-15', '04-2414623', true, '77', 10, 5],
[false, 'Eare', 'China', 'ML Mountain Seat/Saddle 1', '2025-04-04', '15-1917509', false, '199', 9, 4],
[false, 'Yozio', 'China', 'HL Mountain Seat/Saddle 1', '2025-03-15', '06-2526845', true, '149', 8, 2],
[false, 'Quinu', "Xi'an", '425-777-7829', '2025-02-22', '83-1713558', false, '191', 9, 5],
[false, 'Jazzy', 'United Kingdom', 'ML Road Seat/Saddle 1', '2025-08-07', '00-8892524', true, '150', 10, 2],
[false, 'Thoughtsphere', 'China', 'HL Road Seat/Saddle 1', '2025-11-28', '39-5538991', true, '130', 7, 3],
[false, 'Leenti', 'China', 'ML Road Rear Wheel', '2025-12-29', '06-9002973', true, '179', 1, 2],
[false, 'Quaxo', 'United Kingdom', 'HL Road Rear Wheel', '2025-09-06', '73-6104901', true, '98', 5, 3],
[false, 'Tanoodle', 'Chile', 'LL Mountain Seat/Saddle 2', '2025-05-24', '68-7384479', true, '175', 2, 3],
[false, 'Feednation', 'China', 'ML Mountain Seat/Saddle 2', '2025-11-21', '26-7757763', true, '11', 1, 3],
[false, 'Kayveo', 'China', 'HL Mountain Seat/Saddle 2', '2025-06-21', '07-4873562', false, '184', 7, 4],
[false, 'Meevee', 'Saudi Arabia', 'LL Road Seat/Saddle 1', '2025-11-16', '46-5819554', false, '27', 9, 3],
[false, 'Twitterworks', 'China', 'ML Road Seat/Saddle 2', '2025-04-19', '01-2666826', true, '186', 3, 2],
[false, 'Wikizz', 'Tanzania', 'HL Road Seat/Saddle 2', '2025-03-08', '54-7090503', true, '20', 3, 3],
[false, 'Yoveo', 'United States', 'LL Mountain Tire', '2025-10-14', '78-7658520', false, '153', 2, 1],
[false, 'Yakidoo', 'China', 'ML Mountain Tire', '2025-10-12', '23-9926318', true, '161', 8, 5],
[false, 'Oyope', 'China', 'HL Mountain Tire', '2025-09-20', '20-0179517', true, '98', 10, 5],
[false, 'Skipstorm', 'United States', 'LL Road Tire', '2025-10-01', '02-9543343', true, '30', 7, 3],
[false, 'Minyx', 'United States', 'ML Road Tire', '2025-07-07', '98-3938169', true, '73', 10, 2],
[false, 'Miboo', 'China', 'HL Road Tire', '2025-07-25', '68-5197934', true, '158', 9, 1],
[false, 'Realfire', 'United States', 'Touring Tire', '2025-08-27', '39-8260460', true, '122', 5, 2],
[false, 'Shufflester', 'China', 'Mountain Tire Tube', '2025-06-08', '45-9776170', true, '33', 2, 4],
[false, 'Ntag', 'China', 'Road Tire Tube', '2025-12-06', '45-0858451', true, '107', 6, 2],
[false, 'Jabberbean', 'United States', 'Touring Tire Tube', '2025-04-26', '15-4247305', true, '15', 1, 2],
[false, 'Thoughtblab', 'China', 'LL Bottom Bracket', '2025-05-21', '15-8534931', true, '168', 5, 2],
[false, 'Jabbertype', 'China', 'Classic Vest', '2025-07-25', '23-1251557', true, '135', 4, 2],
[false, 'Buzzshare', 'United Kingdom', 'Cycling Cap', '2025-07-07', '86-5920601', true, '11', 1, 4],
[false, 'Roodel', 'United States', 'Full-Finger Gloves', '2025-01-13', '48-1055459', true, '41', 6, 4],
[false, 'Zoovu', 'China', 'Half-Finger Gloves', '2025-06-03', '12-7842022', true, '144', 6, 1],
[false, 'Photofeed', 'China', 'HL Mountain Frame', '2025-07-14', '94-5088099', true, '106', 1, 4],
];
const example = document.getElementById('exampleTheme')!;
const isDark = document.documentElement.getAttribute('data-theme') === 'dark';
const hotInstance = new Handsontable(example, {
theme: getTheme('horizon').setColorScheme(isDark ? 'dark' : 'light'),
data,
height: 450,
colWidths: [180, 220, 140, 120, 120, 120, 140],
colHeaders: ['Company Name', 'Name', 'Sell date', 'In stock', 'Quantity', 'Order ID', 'Country'],
contextMenu: [
'cut',
'copy',
'---------',
'row_above',
'row_below',
'remove_row',
'---------',
'alignment',
'make_read_only',
'clear_column',
] as PredefinedMenuItemKey[],
columns: [
{
data: 1,
type: 'text',
headerClassName: 'htLeft',
},
{
data: 3,
type: 'text',
headerClassName: 'htLeft',
},
{
data: 4,
type: 'intl-date',
locale: 'en-GB',
dateFormat: { day: '2-digit', month: '2-digit', year: 'numeric' },
headerClassName: 'htLeft',
},
{
data: 6,
type: 'checkbox',
className: 'htCenter',
headerClassName: 'htLeft',
},
{
data: 7,
type: 'numeric',
headerClassName: 'htLeft',
},
{
data: 5,
type: 'text',
headerClassName: 'htLeft',
},
{
data: 2,
type: 'text',
headerClassName: 'htLeft',
},
],
dropdownMenu: true,
hiddenColumns: {
indicators: true,
},
multiColumnSorting: true,
filters: true,
rowHeaders: true,
manualRowMove: true,
headerClassName: 'htLeft',
autoWrapRow: true,
autoWrapCol: true,
manualRowResize: true,
manualColumnResize: true,
navigableHeaders: true,
licenseKey: 'non-commercial-and-evaluation',
});
// Theme dropdown
const dropdown = document.getElementById('themeDropdown')!;
const trigger = document.getElementById('themeTrigger')!;
const triggerLabel = document.getElementById('triggerLabel')!;
const triggerColors = document.getElementById('triggerColors')!;
const menu = document.getElementById('themeMenu')!;
const currentTheme = document.documentElement.getAttribute('data-theme') === 'dark'
? 'horizon-dark'
: 'horizon-light';
const setTheme = (value: string) => {
const [themeName, colorScheme] = value.split('-');
hotInstance.updateSettings({ theme: getTheme(themeName).setColorScheme(colorScheme || 'auto') });
// Update trigger label
const item = menu.querySelector(`[data-value="${value}"]`);
if (item) {
triggerLabel.textContent = item.textContent!.trim();
}
// Update trigger color dots by resolving computed grid styles
const hotRoot = document.querySelector('#exampleTheme .handsontable');
if (hotRoot) {
const helper = document.createElement('div');
helper.style.cssText = 'position:absolute;visibility:hidden;pointer-events:none;';
hotRoot.appendChild(helper);
const resolve = (varName: string) => {
helper.style.color = `var(${varName})`;
return getComputedStyle(helper).color;
};
const fg = resolve('--ht-foreground-color');
const bg = resolve('--ht-background-color');
const accent = resolve('--ht-accent-color');
hotRoot.removeChild(helper);
const dots = triggerColors.querySelectorAll<HTMLElement>('.color');
if (dots.length >= 3) {
dots[0].style.background = fg;
dots[1].style.background = bg;
dots[2].style.background = accent;
}
}
// Update aria-selected
menu.querySelectorAll('li').forEach((li) => {
li.setAttribute('aria-selected', (li as HTMLElement).dataset.value === value ? 'true' : 'false');
});
};
// Toggle dropdown
trigger.addEventListener('click', () => {
const isOpen = !menu.hidden;
menu.hidden = isOpen;
trigger.setAttribute('aria-expanded', String(!isOpen));
});
// Select item
menu.addEventListener('click', (e) => {
const item = (e.target as HTMLElement).closest('li[data-value]') as HTMLElement | null;
if (item) {
setTheme(item.dataset.value!);
menu.hidden = true;
trigger.setAttribute('aria-expanded', 'false');
}
});
// Close on outside click
document.addEventListener('click', (e) => {
if (!dropdown.contains(e.target as Node)) {
menu.hidden = true;
trigger.setAttribute('aria-expanded', 'false');
}
});
// Close on Escape
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && !menu.hidden) {
menu.hidden = true;
trigger.setAttribute('aria-expanded', 'false');
trigger.focus();
}
});
// Set initial theme
setTheme(currentTheme);
HTML
<div class="example-controls-container">
<div class="controls">
<div class="theme-dropdown" id="themeDropdown">
<button class="theme-dropdown-trigger" id="themeTrigger" type="button" aria-haspopup="listbox" aria-expanded="false">
<span class="theme-dropdown-colors" id="triggerColors">
<span class="color"></span>
<span class="color"></span>
<span class="color"></span>
</span>
<span class="theme-dropdown-label" id="triggerLabel">Horizon Light</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="themeMenu" role="listbox" hidden>
<li role="option" data-value="main-light">
<span class="theme-dropdown-colors ht-theme-main"><span class="color" style="background: var(--ht-foreground-color);"></span><span class="color" style="background: var(--ht-background-color);"></span><span class="color" style="background: var(--ht-accent-color);"></span></span>
Main Light
</li>
<li role="option" data-value="main-dark">
<span class="theme-dropdown-colors ht-theme-main-dark"><span class="color" style="background: var(--ht-foreground-color);"></span><span class="color" style="background: var(--ht-background-color);"></span><span class="color" style="background: var(--ht-accent-color);"></span></span>
Main Dark
</li>
<li role="option" data-value="horizon-light">
<span class="theme-dropdown-colors ht-theme-horizon"><span class="color" style="background: var(--ht-foreground-color);"></span><span class="color" style="background: var(--ht-background-color);"></span><span class="color" style="background: var(--ht-accent-color);"></span></span>
Horizon Light
</li>
<li role="option" data-value="horizon-dark">
<span class="theme-dropdown-colors ht-theme-horizon-dark"><span class="color" style="background: var(--ht-foreground-color);"></span><span class="color" style="background: var(--ht-background-color);"></span><span class="color" style="background: var(--ht-accent-color);"></span></span>
Horizon Dark
</li>
<li role="option" data-value="classic-light">
<span class="theme-dropdown-colors ht-theme-classic"><span class="color" style="background: var(--ht-foreground-color);"></span><span class="color" style="background: var(--ht-background-color);"></span><span class="color" style="background: var(--ht-accent-color);"></span></span>
Classic Light
</li>
<li role="option" data-value="classic-dark">
<span class="theme-dropdown-colors ht-theme-classic-dark"><span class="color" style="background: var(--ht-foreground-color);"></span><span class="color" style="background: var(--ht-background-color);"></span><span class="color" style="background: var(--ht-accent-color);"></span></span>
Classic Dark
</li>
</ul>
</div>
</div>
</div>
<div id="exampleTheme" class="disable-auto-theme"></div>
CSS
#exampleTheme {
position: relative;
z-index: 0;
}
/* Theme dropdown */
.theme-dropdown {
position: relative;
display: inline-flex;
align-items: center;
}
.theme-dropdown-trigger {
display: inline-flex;
align-items: center;
gap: 0.5rem;
background: none;
border: 1px solid var(--sl-color-gray-5);
color: var(--sl-color-gray-2);
cursor: pointer;
font-size: var(--sl-text-sm);
font-weight: 500;
padding: 0.4rem 0.625rem;
transition: color 0.15s, background-color 0.15s;
white-space: nowrap;
border-radius: 0;
}
.theme-dropdown-trigger:hover {
color: var(--sl-color-white);
background: var(--sl-color-gray-7, var(--sl-color-gray-6));
}
.theme-dropdown-chevron {
flex-shrink: 0;
margin-inline-start: 0.15rem;
transition: transform 0.15s;
}
.theme-dropdown-trigger[aria-expanded='true'] .theme-dropdown-chevron {
transform: rotate(180deg);
}
.theme-dropdown-menu {
background: var(--sl-color-bg-nav);
border: 1px solid var(--sl-color-gray-5);
border-radius: 0;
box-shadow: none;
inset-inline-start: 0;
list-style: none;
margin: 0;
min-width: 100%;
overflow-y: auto;
padding: 0;
position: absolute;
top: 100%;
z-index: 100;
}
.theme-dropdown-menu[hidden] {
display: none !important;
}
.theme-dropdown-menu li {
align-items: center;
color: var(--sl-color-text);
display: flex;
font-size: var(--sl-text-sm);
gap: 0.5rem;
padding: 0.5rem 0.75rem;
cursor: pointer;
border-bottom: 1px solid var(--sl-color-gray-5);
transition: background 0.1s, color 0.1s;
white-space: nowrap;
list-style: none;
margin: 0;
}
.theme-dropdown-menu li .theme-dropdown-colors {
display: none;
}
.theme-dropdown-menu li:last-child {
border-bottom: none;
}
.theme-dropdown-menu li:hover,
.theme-dropdown-menu li:focus-visible {
background: var(--sl-color-gray-6);
color: var(--sl-color-white);
outline: none;
}
.theme-dropdown-menu li[aria-selected='true'] {
color: var(--sl-color-white);
box-shadow: inset 0 0 0 1px var(--sl-color-accent);
}
/* Color dots */
.theme-dropdown-colors {
display: inline-flex;
align-items: center;
gap: 3px;
}
.theme-dropdown-colors .color {
width: 8px;
height: 8px;
border-radius: 50%;
border: 1px solid var(--sl-color-gray-4);
}

Light and dark modes

Each theme comes with three modes:

  • Light mode
  • Dark mode
  • Auto mode

When using the Theme API, you can configure the color scheme using setColorScheme() with 'light', 'dark', or 'auto' values. The 'auto' option allows programmatic control over light/dark switching based on your application’s logic.

When using CSS files, color scheme switching is controlled through CSS class names. Use ht-theme-{name} for light mode, ht-theme-{name}-dark for dark mode, or ht-theme-{name}-dark-auto for automatic switching based on system preferences (e.g., ht-theme-main, ht-theme-main-dark, ht-theme-main-dark-auto).

Available Theme API parameters

When registering a theme with registerTheme() or updating it using the params() method, you can configure the following keys:

KeyDescription
nameTheme name string (can only be set during registerTheme(), cannot be updated via params())
sizingSize scale values (size_0 through size_10)
densityDensity type ('default', 'compact', 'comfortable') or density configuration object
iconsSVG icon definitions
colorsColor palette with nested color values
tokensDesign tokens for visual properties
colorSchemeColor scheme ('light', 'dark', or 'auto')

Token value references

Token values support a powerful reference system using dot notation. Instead of hardcoding values, you can reference values from other configuration namespaces:

Reference patternExampleDescription
Direct value'14px'Use a literal CSS value
tokens.*'tokens.foregroundColor'Reference another token value
sizing.*'sizing.size_4'Reference a sizing scale value
colors.*'colors.primary.500'Reference a color from the palette
density.*'density.gap'Reference a density-specific value

Sizing references

The sizing scale provides consistent spacing values:

myTheme.params({
tokens: {
iconSize: 'sizing.size_5', // References e.g. 20px
wrapperBorderRadius: 'sizing.size_2', // References e.g. 8px
},
});

Color references

Colors use a hierarchical structure with dot notation for nested values:

myTheme.params({
tokens: {
accentColor: 'colors.primary.500', // References primary color
borderColor: 'colors.palette.200', // References palette color
backgroundColor: 'colors.white', // References base white color
},
});

Density references

Density values adjust spacing based on the selected density type:

myTheme.params({
tokens: {
cellHorizontalPadding: 'density.cellHorizontal',
buttonVerticalPadding: 'density.buttonVertical',
gapSize: 'density.gap',
},
});

Token cross-references

Tokens can reference other tokens for consistent styling:

myTheme.params({
tokens: {
barForegroundColor: 'tokens.foregroundColor',
cellEditorBackgroundColor: 'tokens.backgroundColor',
},
});

Light and dark mode values

For tokens that should have different values in light and dark modes, use an array with two values where the first value is for light mode and the second is for dark mode:

myTheme.params({
tokens: {
// [lightModeValue, darkModeValue]
borderColor: ['colors.palette.100', 'colors.palette.700'],
foregroundColor: ['colors.palette.800', 'colors.palette.200'],
backgroundColor: ['colors.white', 'colors.palette.950'],
},
});

Available CSS files

Handsontable provides CSS files needed to style your data grid. Here’s an overview of what’s available:

Base CSS file

  • handsontable.css / handsontable.min.css - The base stylesheet containing all structural styles, layout rules, and core functionality. This file is auto-injected by default. You can inject it manually instead, but you must set injectCoreCss to false first. It includes border styles, cell rendering rules, and other fundamental grid components.

Theme files

All themes are available in two variants:

  • ht-theme-{name}.css / ht-theme-{name}.min.css - Complete theme with icons included (where {name} is main, horizon, or classic).
  • ht-theme-{name}-no-icons.css / ht-theme-{name}-no-icons.min.css - Theme without icon styles.

Icon files

If you’re using a theme without icons (*-no-icons.css), you can optionally load separate icon files:

  • ht-icons-{name}.css / ht-icons-{name}.min.css - Icon styles for the theme (where {name} is main or horizon).

For production, use the minified versions (.min.css) to reduce file size and improve load times. For development, you may prefer the unminified versions (.css) for easier debugging.

Use a theme

There are two ways to apply a theme. The recommended approach is to use the Theme API with a theme object, which provides full access to runtime configuration features like density modes and color schemes.

The Theme API allows you to import and register themes programmatically. This approach provides runtime access to theme customization features.

  1. Import a theme

    import Handsontable from 'handsontable';
    import { classicTheme } from 'handsontable/themes';
    const hot = new Handsontable(container, {
    theme: classicTheme,
    // ... other options
    });
  2. Configure the theme

    You can configure the theme before creating the instance using the builder pattern:

    import { mainTheme, registerTheme } from 'handsontable/themes';
    const theme = registerTheme(mainTheme)
    .setColorScheme('auto') // 'light', 'dark', or 'auto'
    .setDensityType('comfortable'); // 'default', 'compact', or 'comfortable'
    const hot = new Handsontable(container, {
    theme,
    });

    Or configure it after init by retrieving the registered theme with getTheme() (the theme is registered when you pass it to the config):

    import { mainTheme, getTheme } from 'handsontable/themes';
    const hot = new Handsontable(container, {
    theme: mainTheme,
    });
    getTheme('main')
    ?.setColorScheme('auto')
    ?.setDensityType('comfortable');

    UMD build (script tags)

    When using Handsontable via CDN or script tags, load the theme script after the main Handsontable script. The theme auto-registers itself, and you can retrieve it using getTheme():

    <script src="https://cdn.jsdelivr.net/npm/handsontable/dist/handsontable.full.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/handsontable/dist/themes/main.min.js"></script>
    <script>
    // The theme is auto-registered when the script loads
    // Retrieve it using getTheme()
    const theme = Handsontable.themes.getTheme('main')
    .setColorScheme('auto')
    .setDensityType('default');
    const hot = new Handsontable(document.getElementById('container'), {
    theme: theme,
    // ... other options
    });
    </script>

Option 2: Using CSS files

Alternatively, you can load theme CSS files and pass the theme name as a string to the theme option.

  1. Load CSS files

    To ensure Handsontable renders correctly, it’s required to load both the base and theme CSS files. The base file contains structural styles, while the theme file includes colors, sizes, and other variables needed for the grid.

    // ESM (ECMAScript modules)
    import 'handsontable/styles/ht-theme-main.min.css';
    // CommonJS
    require('handsontable/styles/ht-theme-main.min.css');

    Alternatively, you can import the necessary files from the recommended CDN such as JSDelivr or cdnjs.

    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/handsontable/styles/ht-theme-main.min.css" />
  2. Pass a theme name

    To use a theme, specify the theme name in the data grid’s global settings object:

    const container = document.querySelector('#handsontable-example');
    const hot = new Handsontable(container, {
    // theme name with obligatory `ht-theme-*` prefix
    theme: 'ht-theme-main',
    // other options
    });

The legacy theme

The legacy CSS file (handsontable.full.min.css) was the default styles up until version 15 (released in December 2024). These styles are legacy and are removed in version 17.0.0.

Known limitations

In some cases, global styles enforced by the browser or operating system can impact the appearance of the data grid. This is a common challenge faced by all websites, not just Handsontable. Here are two specific scenarios and how to handle them:

  • High contrast mode in Windows: To style the component when Windows’ high contrast mode is active, use the forced-colors media query. This allows you to detect and adapt to forced color settings. Read more
  • Auto dark theme in Google Chrome: Chrome automatically applies a dark theme in some scenarios. To detect and manage this behavior, refer to the official Chrome guide

Troubleshooting

Didn’t find what you need? Try this: