Handsontable Releases

Learn what's new across releases, with details of features, enhancements, and resolved issues.

Jan
Feb
Mar
Apr
May
Jun
Jul
Aug
Sep
Oct
Nov
Dec
Handsontable 18.1.1Sep 15, 2026
Fixes
  • Fixed problem where copying, cutting, and pasting was doing nothing in a grid embedded in a Salesforce Lightning Web Component.
  • Fixed a filters plugin crash thrown when a cell in a filtered column was edited, or the data was replaced with a filter active, after updateSettings() was called with the filters option (as the React and Angular wrappers do on every update).
Handsontable 18.1.0Sep 1, 2026
Features
  • Added the modifySinglePassLayout hook, which forces the previous measure-then-render layout path for a table.
  • Added the selectionHandles option, which shows draggable handles at each edge midpoint of a selected range for resizing the selection, and the moveCells option for moving a cell selection to a new location by dragging its border.
  • Added Persian language RTL direction support.
  • Added support for entitlement license keys, and made a missing or invalid license key block the grid with a modal that cannot be closed.
  • Added the intl-datetime cell type, with a native date-time picker, Intl.DateTimeFormat display via the new dateTimeFormat option, and column sorting, filtering, and Excel export support.
  • Added the preserveNumericLiteral option to the numeric cell type, which keeps the exact value you type in the cell editor (preserving trailing decimal zeros and large-number precision).
  • Added the colorScheme and density options, which set the color scheme and the spacing of the grid without declaring a theme.
  • Added a public API to the NestedRows plugin for collapsing and expanding parent rows: collapseAll(), expandAll(), collapseParent(), expandParent(), toggleParent(), getCollapsedParents(), isParentCollapsed(), isParent(), getRowLevel(), getRowParent(), countChildren(), expandToRow(), and expandToLevel(), plus the new beforeRowCollapse, afterRowCollapse, beforeRowExpand, and afterRowExpand hooks. The plugin also no longer loses its collapsed rows on an updateSettings() call, and no longer throws when asked for the index, parent, or nesting level of a row object held from before a loadData() or updateData() call.
  • Added the formulas.hyperlinks option, which renders cells holding a HYPERLINK formula as clickable links.
  • Added the SanitizerContext type, which lists the write surfaces the sanitizer option receives.
  • Reduced memory usage when scrolling large datasets by releasing cell metadata for rows scrolled out of the viewport.
  • Improved the performance of sorting, filtering, and row/column hiding on large datasets by speeding up internal index translation.
  • Improved initialization time for large date-typed datasets by validating ISO dates arithmetically instead of constructing a Date object for every cell.
  • Improved initial render time and memory usage for large datasets with uniform row heights and column widths.
  • Improved filtering performance on large datasets by writing the row-trimming state in a single bulk operation instead of scanning and updating it row by row.
  • Improved vertical scrolling performance for grids with pinned bottom rows (fixedRowsBottom).
  • Improved the performance of sorting, filtering, hiding, and inserting/removing rows/columns on large datasets by removing redundant per-operation work in the index translation layer.
  • Improved the performance of the internal hooks (events) dispatch: each hook's callbacks are now stored in a linked list, and removing a hook frees it immediately instead of accumulating skipped entries that were iterated over on every run.
  • The grid now renders in a single pass, predicting whether scrollbars will appear instead of rendering, measuring, and re-rendering. The mergeCells plugin keeps the previous path, and the modifySinglePassLayout hook opts out of it.
  • Improved vertical scrolling performance by skipping the column header re-render when the column layout is unchanged.
  • Improved vertical scrolling performance by re-rendering only the rows entering the viewport and reusing the rows that stay.
  • Improved scrolling performance for grids embedded in complex pages by keeping the rendered cells in place during scroll instead of re-inserting them.
  • Improved scrolling smoothness: the auto mode of viewportRowRenderingOffset and viewportColumnRenderingOffset now extends the rendered viewport dynamically in the scroll direction, so consecutive scroll steps reuse already-rendered cells.
  • Improved the performance and memory usage of the cell meta storage. Reading cell settings stays fast on large grids. Memory used by settings of scrolled-away rows is released. Bulk operations - sorting, copying, pasting, filling, exporting, validation, checkbox toggling, and automatic size sampling - no longer permanently cache settings for every visited cell. Added the getCellMetaTransient() method for reading the effective cell configuration without caching it.
  • Improved memory usage of read-only cell settings lookups. The context menu, filters dropdown, formulas synchronization, data type detection, column summaries, and clearing large selections no longer permanently cache settings for every visited cell.
  • Improved scroll-to-row performance on large grids: keyboard navigation and scrollViewportTo near the bottom of the dataset now compute the scroll offset in constant time instead of summing every row height from the top.
  • Improved performance of copy, autofill, bulk edits, header resize, and custom borders on large selections by replacing linear membership scans with set-based lookups.
  • Improved rendering performance of grids with wrapped or content-sized rows: the row-height cache is no longer rebuilt on every render, edit, or scroll step when row heights have not changed, and rebuilds triggered by newly measured rows now scale with the number of taller rows instead of the total row count.
  • Improved index-map performance: writing an unchanged value into scalar-valued index maps (hiding, trimming, order sequence, and the automatic or manual row/column size maps) no longer rebuilds the index caches.
  • Improved the performance of the MergeCells plugin: scrolling, extending large selections, and moving the focus with the keyboard no longer slow down with the grid or selection size.
  • Improved Formulas plugin performance on large grids: faster rendering and sorting through cached index translations, and bulk row/column removals now issue batched engine calls.
  • Improved sorting performance on large datasets: date and time columns no longer re-parse values on every comparison, and text columns no longer re-lowercase values on every comparison.
  • Improved Filters plugin performance: batch changes into a filtered column update the filter list once per column instead of once per changed cell, importing conditions updates once per column instead of once per condition, and applying filters no longer re-reads rows already rejected by earlier columns.
  • Improved the performance of bulk grid alterations: removing many rows or columns, inserting multiple columns, pasting data that extends the grid, and edits with the minSpareCols or minCols options on large datasets.
  • Improved getColHeader performance: the index translation derived from the columns function is now cached instead of being rebuilt on every call, which also speeds up grid initialization and updateSettings on grids with many columns.
  • Improved sizing plugins performance: AutoColumnSize measures only the changed cells on edits instead of rescanning whole columns, its background width sweep is budgeted and runs on browser idle time, AutoRowSize reuses the cached header height on selection-driven renders, and a multi-column resize triggers one height recalculation instead of one per column.
  • Changed how touch opens the cell editor on devices that report both touch and mouse input, such as iPad: a double-tap opens it (taps up to 1 second apart), and a single tap on an already selected cell no longer opens it as it did in 18.0.0, where duplicated mouse events after the tap triggered the double-click.
  • Removed leftover Pikaday styles from the themes.
Fixes
  • Fixed a Sheet size limit exceeded error thrown when grids sharing one HyperFormula instance used different maxRows values. The grid's maxRows and maxCols no longer limit the engine, so an oversized insert is now capped at the limit instead of being rejected.
  • Fixed a bug where dragging a selection handle to the edge of the grid on a mobile device did not scroll the viewport, so the selection stopped growing at the last visible cell.
  • Fixed the columnSummary plugin misbehaving when another plugin trimmed rows (for example a collapsed nestedRows group): false out-of-bounds warnings, results written into the wrong row, a crash when the destination row was hidden, and hidden summary rows being counted as data by other summaries.
  • Fixed the 'filter by value' list dropping the values that the column's own filter condition filtered out, which made them impossible to select again.
  • Fixed open menus (context menu, dropdown menu, and filters) becoming detached from the grid when a scrollable parent container or the grid viewport was scrolled. Menus now follow their anchor and close when it scrolls out of view.
  • Fixed a nested header bug where a wide column group's label jumped while scrolling horizontally.
  • Fixed an error thrown by countRowHeaders() and countColHeaders() when they were called while the table view was unavailable (during initialization or after the instance was destroyed).
  • Fixed merged cells breaking after filtering, trimRows/nestedRows collapse, or sorting - merges now stay whole and follow the visible rows.
  • Fixed nested headers not following the data when moving columns; labels now travel with their columns, a group split across non-adjacent columns renders as separate banners, and a collapsed group either follows the move or expands when the move would split it.
  • Fixed nested column headers ignoring stopImmediatePropagation() called in the beforeOnCellMouseOver hook, so a header drag-selection can now be blocked the same way as for regular column headers.
  • Fixed stale cell metadata left behind after unmerging cells, which made toHTML() output extra cells.
  • Fixed undo not restoring merged cells after removing a column that overlapped a merge.
  • Fixed missing selection border edges and active header highlight accents along frozen-pane boundaries (fixedRowsTop / fixedRowsBottom / fixedColumnsStart).
  • Fixed context menu inserting two rows instead of one when tapping 'Insert row above' or 'Insert row below' on iPad (Safari).
  • Fixed multiselect cell type displaying a neighbouring column's value after moving the column with manualColumnMove.
  • Fixed a bug where Ctrl/Cmd+clicking an already-selected cell in a multi-cell selection caused the active highlight to jump to a different cell.
  • Reduced memory usage when filtering large datasets: the Filters plugin no longer permanently retains a cell meta object for every source row of each filtered column.
  • Fixed an error thrown when removing a frozen column while using the legacy fixedColumnsLeft option.
  • Fixed the multiselect cell type so its option filtering folds case in a locale-aware way, consistent with the autocomplete editor, for Turkish, Azeri, and Lithuanian locales.
  • Fixed the horizon theme row header background color not cascading from headerRowBackgroundColor for even rows.
  • Restored the multiSelect camelCase alias for the MultiSelect cell type, so both multiselect and multiSelect resolve to the same cell type.
  • Fixed a load-time slowdown where loadData() and updateData() resolved cell metadata for the entire dataset when a cells function was configured, even with no validators defined.
  • Fixed the multiselect cell type not firing beforeChange and afterChange (or running validation) when a value was removed by clicking the chip's X icon.
  • Fixed date filter conditions returning no results by rendering native date and time inputs in the filter menu instead of free-text fields.
  • Fixed an autofill bug where dragging the fill handle of the last column slightly diagonally, past the table's edge, drew the fill border but did not apply the values.
  • Fixed the modifyCopyableRange hook's type declaration to reflect that its return value is used.
  • Fixed the ContextMenu.SEPARATOR constant missing from the TypeScript type declarations.
  • Fixed a doubled separator line under the 'Filter by value' list in the dropdown menu.
  • Fixed removing rows throwing a 'Maximum call stack size exceeded' error on grids with roughly 125,000 rows or more.
  • Fixed the Events type resolving every hook callback to any, so callbacks typed through Events[hookName] and addHook now get correct parameter types and autocomplete.
  • Restored the horizon theme alternating stripe on even row headers, which stopped showing after the even row header background was made to cascade from headerRowBackgroundColor.
  • Fixed a grid configured with a width and no height collapsing vertically. Relative widths (100%, percentages, viewport units, calc() with them) keep scrolling horizontally with the window, while definite pixel/em widths clip to their box.
  • Fixed autofill dragging near vertically merged cells - the fill handle can now target rows inside a merged band in a neighboring column instead of jumping past the whole merge.
  • Fixed an error thrown on touch devices when the grid was destroyed while a momentum scroll was still coasting.
  • Fixed custom editors created with editorFactory throwing an error when shortcuts were defined without shortcutsGroup.
  • Fixed an error thrown on every focus change when the grid failed to initialize.
  • Fixed a failed fetchRows call in the dataProvider plugin surfacing as an unhandled promise rejection.
  • Fixed the autocomplete editor and several internal utilities using Array.prototype.toSorted and Array.prototype.at, which are not available in every browser the library declares support for.
  • Fixed an Error: TR was expected to be rendered but is not thrown when a beforeDraw hook cancels a render after the rendered row range has already advanced.
  • Fixed a spurious scrollbar appearing when a cell in the last column or row was selected in a grid with frozen columns or rows - the fill handle now stays inside the viewport instead of overflowing it.
  • Fixed the Formulas plugin leaving its internal redo state active after a redo operation, which made the plugin treat subsequent operations as undo/redo replay.
  • Fixed the grid's cell styling (borders, padding, background, box-sizing, and corner radius) leaking into a table or other element rendered inside a cell.
  • Fixed several CustomBorders bugs: borders now follow their cell when rows or columns are inserted, removed, or moved (and stay removable from the context menu afterward), and ranges defined with a top-level border object - including overlapping ranges - now render with the configured width, color, and style. Also added the customBordersProgressive option, which applies a large customBorders configuration in background batches so the grid renders immediately, together with the afterCustomBordersUpdate hook that fires once all custom borders have been applied.
  • Fixed the license notification bar content overflowing on grids narrower than 300px.
  • Fixed Handsontable errors not carrying their identifying cause on Safari below 15.
  • Fixed rows losing their alignment when the tallest cell in a row was in a frozen column.
  • Fixed a formula cell staying marked as invalid after an undo or redo restored a correct value.
  • Fixed being unable to move a column by dragging its header while column sorting is enabled. Sorting by clicking a header now happens on mouse up instead of mouse down, and only the header label and its sort indicator sort on click. A click on a column header no longer fires beforeColumnMove and afterColumnMove, which previously ran even though no column moved.
  • Fixed mobile selection handles not being visible on touch devices.
  • Fixed the full bundle failing to load on pages with an AMD loader present (RequireJS, SharePoint).
  • Fixed an error thrown when a grid initialized in a hidden container was destroyed right as it became visible.
  • Fixed autoRowSize and autoColumnSize measuring cells without the styling applied through their className and without the formatting applied by the renderer's own valueFormatter, which left the row headers misaligned with the data cells.
  • Fixed grid interactions inside Shadow DOM and sandboxed hosts such as Salesforce Lightning Web Components: the cell editor no longer closes when clicked, copy and paste work, focus is released when it leaves the grid, outside clicks clear the selection, and the grid's internal z-index values no longer paint above the host page UI.
  • Fixed the dropdown and autocomplete editor list rendering narrower than the edited column when trimDropdown is set to false.
  • Fixed the fixedRowsTop, fixedRowsBottom, and fixedColumnsStart options losing their value after undoing a row or column insertion, which un-pinned a frozen row or column.
  • Fixed autofill dragging near horizontally merged cells - the fill handle can now target columns inside a merged band in a row below the merge instead of snapping back to the merge anchor.
  • Fixed the autocomplete and dropdown option list collapsing to zero height, hiding every option, when less than one option of space was left below the edited cell.
  • Fixed misaligned column headers and frozen columns, and a grid that filled its container only after a click, when it was created in a container that had no size yet.
  • Fixed a memory leak where each loadData() or updateData() call added another formula engine sheet, so repeated data loads kept slowing the grid down.
  • Fixed the autofill fill handle being cut in half at the bottom freeze line.
  • Fixed a bug where the afterSelectionEnd and afterSelectionEndByProp hooks fired twice when selecting rows or columns by dragging over their headers.
  • Vue: Fixed the Vue 3 wrapper not updating the grid when HotColumn components were added, removed, or reordered dynamically.
  • React: Fixed a memory leak in the React wrapper where the portal containers used by component-based renderers were retained for every scrolled cell instead of being released once a cell left the viewport.
  • React: Fixed missing TypeScript autocomplete and type checking for <HotTable> and <HotColumn> props.
  • Angular: Fixed custom editor shortcut groups in the Angular wrapper.
  • Fixed the grid no longer responding to keyboard shortcuts after clicking focusable plugin UI placed in the grid's layout slots (for example, pagination controls).
  • Fixed an open cell editor staying visible over an unrelated row, and committing to a row you could no longer see, when the edited cell was hidden - for example by turning a Pagination page or hiding its row or column.
  • Fixed an expired trial license key not blocking the grid when the key carried the no-ui-warns flag.
  • Security:
    • Fixed pasted HTML being able to run scripts, and applied the sanitizer option to the surfaces that previously skipped it.
    • Fixed the dialog plugin interpolating an unvalidated template.id into the dialog's id attribute, and the loading plugin rendering its title and description options as HTML. Those two loading options now render as text, so markup passed in them shows up literally instead of being interpreted; use icon for markup. The button type option of the dialog, emptyDataState, and notification plugins is now resolved where the markup is built.
Handsontable 18.0.0Major versionJun 30, 2026
Features

A core rewritten in TypeScript

  • The entire Handsontable core has been rewritten from JavaScript to TypeScript. This is a long-term investment in stability and developer experience that touches nearly every part of the codebase.
  • The rewrite brings strict-mode compatibility, type-aware lint rules, and stronger typings for built-in renderers, editors, and validators - the cell properties passed into them now use the canonical CellProperties interface instead of a loose record type. It also folded Walkontable, the rendering engine, into the main type-check program and cut its as unknown escape-hatch casts by roughly 85%.
  • For everyone consuming the published package, the broken and ambiguous type declarations that previously tripped up ESM and CJS setups have been fixed. The shipped .d.ts files are now compatible with TypeScript 5.1 and later, so editor autocompletion and type checking should work out of the box.
  • Because this is a foundational change to how the source is written and typed, it's a breaking change. Most applications will not need code changes, but stricter type definitions can surface issues that were previously hidden.

Faster rendering and dramatically lower memory use

  • Walkontable now renders directly to the DOM, and Handsoncode removed the old diffing renderer entirely. This cut the grid's memory footprint by roughly 50%, reduced memory use during scrolling, and resolved potential out-of-memory errors on very large datasets. On top of that, it no longer eagerly materializes cell metadata for the whole grid on load - it's computed lazily as cells are actually used. For large datasets the impact is substantial: on a 100,000 x 100 grid, around a 90% reduction in memory and roughly 30x faster initial render time.
  • Cell meta set imperatively with setCellMeta (for example, readOnly) is no longer reset by updateSettings.
  • Faster repeated getSourceData() calls inside the cells function and custom renderers, cached overlay alignment calculations, and faster locale-aware text comparisons across search, filtering, sorting, autocomplete, and checkbox rendering.

A new layout system

  • Handsontable 18.0.0 introduces a new layout option that lets you control the order of UI elements rendered around the grid, such as pagination and the dialog.
  • To make this possible, built-in UI now mounts into dedicated wrapper containers rather than being injected ad hoc, which changes the root DOM structure - a breaking change to be aware of if you target Handsontable's markup with custom CSS or scripts.
  • The license notification always renders last in the bottom area and is not orderable, and the Notification plugin now renders its toasts in the grid's overlay layer for more reliable positioning.

A smaller, dependency-free bundle

  • Removed four runtime dependencies - numbro, moment.js, DOMPurify, and handsontable/pikaday - in favor of lighter, built-in implementations. This trims the bundle and reduces the surface area for transitive security and maintenance issues. Removing these is a breaking change for anyone relying on them indirectly through Handsontable.
  • Retired some long-deprecated APIs:
    • The PersistentState plugin, its persistentState option, and its associated hooks have been removed.
    • The Core-level undo/redo methods (hot.undo(), hot.redo(), and friends) are gone. Use hot.getPlugin('undoRedo') instead.

A modernized Angular wrapper

  • The Angular wrapper has been modernized to align with current Angular versions.

New configuration options

  • This release adds a few new ways to configure the grid:
    • hashRevealDelay on the password cell type briefly shows each character as it's typed before masking it.
    • visibleWhen on nested headers ('collapsed', 'expanded', or 'always') lets you choose which columns of a collapsible group stay visible in each state.
    • Public types such as CellMeta, CellProperties, ColumnSettings, and GridSettings are now re-exported as named top-level imports from handsontable and handsontable/base, so you no longer have to dig through internal paths to type your code.

Theming changes

  • Simplified the border-related theme variables. The --ht-wrapper-border-radius variable has been renamed to --ht-border-radius, and the --ht-wrapper-border-width and --ht-wrapper-border-color variables have been removed. If you reference these in a custom theme, you will need to update them when upgrading.

Interaction improvements

  • A few changes make everyday interactions feel closer to a desktop spreadsheet. Dragging a selection past the edge of the viewport now scrolls the grid and extends the selection to the newly revealed cells, and the autofill selection direction now matches the drag direction, the way Google Sheets and Excel behave.

Documentation: new guides, recipes, and a Vue section

  • This release adds documentation for its biggest changes. Layout slots is the counterpart to the new layout system, showing how to place custom UI in the wrapper slots around the grid and control the order of elements in each slot. TypeScript types is a complete reference of the types and interfaces Handsontable now exports, with usage examples for settings, hook callbacks, custom renderers, and editors. And a new Migrating from 17.1 to 18.0 guide walks through removing the moment.js, Pikaday, numbro, and DOMPurify dependencies and updating your TypeScript imports.
  • Vue 3 now stands alongside React and Angular as a first-class framework in the docs, with its own dedicated section and new examples. Vue developers get first-party guidance written for Vue, instead of adapting the vanilla JavaScript examples.
Handsontable 17.1.0May 19, 2026
Features

Export to Excel

  • The new Export to Excel feature lets you save the rendered table to an XLSX file - directly from the grid.
  • The export respects what the user is actually looking at: column order, visible columns, applied filters, sort order, frozen rows/columns, and merged cells all carry over into the generated file. Cell types map to their Excel equivalents where possible (numeric, date, checkbox), so the file opens cleanly in Excel, Google Sheets, and Numbers.
  • Export is triggered through the plugin API and can be wired to any UI control - a toolbar button, a context menu item, or a keyboard shortcut. For large datasets, the operation runs asynchronously so the UI stays responsive, which pairs naturally with the new Notifications plugin to confirm completion.

Server side row model

  • Server-Side Row Model is a built-in integration layer for backend-driven tables. Instead of loading the full dataset into the browser, the grid requests only the rows it needs and delegates sorting, filtering, and pagination to your server. This dataProvider plugin defines a clear CRUD contract that your backend implements:
    • Create - persist newly inserted rows.
    • Read - fetch a page of rows with the current sort, filter, and range state.
    • Update - persist edited cells.
    • Delete - persist removed rows.
  • Each operation receives a structured request describing the grid's current state and expects a structured response, so you can plug the contract into REST, GraphQL, or any transport of your choice. State management, optimistic updates, and reconciliation after failed requests are all handled by the plugin.
  • The result is that datasets of hundreds of thousands of rows become viable without custom virtualization or hand-written adapter code - and the integration pattern is consistent across projects, which matters when the same team ships multiple data-heavy apps.

Notifications (Toasts)

  • The new Notification plugin adds a built-in toast system for communicating status to users. It supports four types out of the box - info, success, warning, and error, along with configurable positioning, duration, and dismiss behavior.
  • Notifications can also be triggered from custom cell types, hooks, and plugins, making them useful well beyond this release's features.

Theme Builder: AI Assistant

  • Theme Builder now includes an AI chat assistant that can generate themes and sample data from natural-language prompts. Ask it for 'a dark theme matching the Stripe dashboard' or 'a high-contrast theme for financial reporting' and it produces a Themes API token set you can preview, tweak, and export.
  • This builds on the Themes API foundation introduced in 17.0, the assistant writes token mappings, not CSS overrides, so generated themes stay compatible with density modes, light/dark switching, and the rest of the theming system.

Docs Platform Updates - Migration to Astro Starlight

  • Migrated handsontable.com/docs from VuePress 1 to Astro Starlight. The user-facing benefits:
    • Faster page loads and smaller JS payloads (island architecture, zero-JS by default).
    • Improved AI/LLM readability - code blocks and content are server-rendered as static HTML, which makes the docs easier to index and cite by tools like Claude, ChatGPT, and Perplexity.

Docs AI Assistant

  • The docs now include an AI Assistant that can answer questions, generate code samples, and help you find the right docs pages on any topic. Just click the Ask AI button to begin chatting.

New Recipes

  • Added over 40 new recipes with detailed examples on Accessibility & UX, Real-time & Integrations, Column and Data Management, Cell Types, Context Menu, Import/Export, Editing and Validation, Filtering and Search, Rendering and Styling, Performance, and Themes.

Accessibility Conformance Report (VPAT)

  • Published an Accessibility Conformance Report based on the VPAT 2.5 template, documenting Handsontable's conformance with WCAG 2.1 AA, Section 508, and EN 301 549. This is aimed at procurement, government, and enterprise customers who need formal accessibility documentation as part of their vendor evaluation.

Performance

  • Performance is one of the top priorities, and 17.1 ships meaningful improvements for large datasets.
Handsontable 17.0.1Mar 25, 2026
Features
  • Improved clipboard processing after pasting.
  • Added missing Core typings so the public API is correctly declared in TypeScript.
Fixes
  • Fixed UndoRedo crash when nullified changes.
  • Fixed UndoRedo beforeChange order.
  • Fixed a bug where the editor does not receive the user inputs.
  • Fixed scrollbar width calculation on Safari >=26.
  • Fixed rounded corners that may be applied in incorrect use cases.
  • Fixed undo/redo stack desync with formulas engine.
  • Fixed column width calculation for collapsed columns.
  • Fixed an issue with table background overflow.
  • Fixed column header misalignment when nestedRow is enabled.
  • Fixed an issue with mobile select handles styles.
  • Fixed an issue with scrolling issue that occurs when preventOverflow is enabled.
  • Fixed date picker positioning near viewport edges.
  • Fixed an issue with main theme hover on menu icon background color.
Handsontable 17.0.0Major versionMar 9, 2026
Features

Native support for MultiSelect Cell Type

  • The new MultiSelect cell type is designed for scenarios where a single field needs to store multiple values. Now you can select any number of options within the same cell from the dropdown menu that supports search, filtering, and full keyboard navigation.
  • Once selected, values are stored directly in the table's source data as an array, keeping the data model clean.
  • Each selected item is rendered inside the cell as a removable chip, making it easy to review or adjust selections made.
  • The multiSelect cell type offers a flexible set of options so you can adjust both its behavior and presentation:
    • allowEmpty: Defines whether an empty selection is considered valid (enabled by default).
    • placeholder: Shows informative text in the cell when no values are selected.
    • visibleRows: Sets how many options are visible at once in the dropdown list.
    • maxSelections: Restricts the number of items a user can choose.
    • sourceSortFunction: Applies custom logic to control how options are ordered.
    • enterCommits: Specifies whether pressing Enter finalizes the selection and closes the editor.
    • searchInput: Enables or disables the built-in search field within the dropdown.
    • filteringCaseSensitive: Determines whether the search function distinguishes between uppercase and lowercase characters.

Simpler Definition for Custom Cell

  • Previously, building a custom editor often meant extending base classes, understanding the full editor lifecycle, and writing repetitive setup code. With the new rendererFactory and editorFactory helpers, you start with a small, working cell definition and extend it only where needed. The factory automatically manages container creation, positioning over the active cell, lifecycle methods (init, beforeOpen, afterOpen, afterClose), and keyboard shortcut handling.

New Themes API: Flexible and Design Systems Friendly Foundation

  • This release introduces a new Themes API that makes styling your table more structured, flexible, and aligned with modern design systems. The new theme architecture is built around four core elements: Token Mapping, Icons, Colors and Light/Dark Mode. Instead of relying on scattered CSS overrides, themes now use a consistent token model with scalable color palettes (100-900, similar to Tailwind or Material Design) and size tokens based on an 8px grid system (size-1 = 8px, size-2 = 16px, etc.). This color and sizing model now makes it much easier to connect Handsontable with existing design systems.
  • A new Density feature lets you switch between compact, default, and comfortable spacing modes, helping you adapt the table to different UX needs without rewriting styles. Styling can still be customized through CSS variables, but now also via a Token API in JavaScript, enabling runtime adjustments and better flexibility.
  • This release also focused on simplifying theme setup. The Main theme is now applied automatically, so there's no need to manually declare a theme or import separate CSS files. In previous versions, you had to include handsontable.js, handsontable.css, and a separate theme stylesheet, but now you only need handsontable.js to get started, reducing setup steps and making integration cleaner.

Theme Builder Updates

  • Alongside the Themes API improvements, the Theme Builder has been updated to align with the new token structure. It now features a redesigned interface that is cleaner and more intuitive to navigate. With clear token descriptions, per-token reset options, and real-time previews, it's easier to understand how each change impacts the table design and make full use of Theme Builder capabilities.

Dependency Reduction

  • Version 17.0.0 focuses on reducing external dependencies to optimize bundle size, improve loading performance, and give you more control over the runtime environment.
  • Numeric Formatting - Numeric cells now use the native Intl.NumberFormat API. The previous pattern and culture options based on numbro.js are deprecated in 17.0.0 and will be removed in the next major release.
  • Date and Time Formatting - Date and time cells now use intl-date and intl-time, powered by Intl.DateTimeFormat. String-based Moment.js formats are deprecated and scheduled for removal in the next major version. This change improves performance and aligns formatting with modern, locale-aware APIs.
  • Built-in HyperFormula - The built-in HyperFormula engine used by the Formulas plugin is deprecated in 17.0.0 and will be removed from package.json in version 18.0. Handsontable will no longer bundle HyperFormula by default. If you use the Formulas plugin, you should install HyperFormula directly in your project and pass it to the plugin configuration (with licenseKey: 'internal-use-in-handsontable'). This change reduces the default package size and gives you control over the formula engine version used in your application.
  • HTML Sanitization - Introduced a new sanitizer option that lets you define your own HTML sanitization logic. DOMPurify is still used by default in 17.0.0 (with a deprecation warning), but it will be removed in the next major release.
  • Polyfills - In 17.0, Handsontable no longer includes core-js, which previously provided polyfills for older JavaScript features. Handsontable now targets modern JavaScript environments. If you need to support older browsers, you can add the required polyfills in your own application setup, based on your specific compatibility requirements.