import current from '/version.js';
import CodeBlock from '@theme/CodeBlock';
:::danger pass
**Handsontable has relicensed away from open source!**
The original MIT license still applies to version `6.2.2`.
After adding the new `licenseKey` requirement, basic integrations still work
with version `17.1.0`.
:::
[Handsontable](https://handsontable.com/) is a robust JavaScript data grid.
The following live integrations use the standalone build:
- [Version `6.2.2` (MIT Licensed)](pathname:///handsontable/)
- [Version `17.1.0` (Proprietary)](pathname:///handsontable/non-oss.html)
:::note Tested Deployments
This demo was tested in the following environments:
| Browser | Date |
|:-------------|:-----------|
| Chromium 148 | 2026-06-09 |
| Safari 18.2 | 2026-06-09 |
:::
:::tip pass
This demo barely scratches the surface. The underlying grid component includes
many additional features that work with [SheetJS Pro](https://sheetjs.com/pro).
:::
## Integration Details
[The "Frameworks" section](/docs/getting-started/installation/frameworks) covers
installation with Yarn and other package managers.
Using the `npm` tool, the following command installs SheetJS and Handsontable:
{`\
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz handsontable@6.2.2`}
Methods and components in both libraries can be loaded in pages using `import`:
```js
import { read, utils, writeFile } from 'xlsx';
import Handsontable from 'handsontable';
import 'handsontable/dist/handsontable.css'
```
:::info pass
Official framework wrapper packages (e.g. `@handsontable/react-wrapper`) should
be used in greenfield projects, as they encapsulate best practices.
Due to framework volatility, the pre-baked integrations may not work with older
versions of ReactJS.
:::
### Internal State
Handsontable uses [arrays of arrays](/docs/api/utilities/array#array-of-arrays)
under the hood by default. This is the most flexible approach for processing
arbitrary data.
If a schema is passed when the table is constructed, Handsontable will use
[arrays of objects](/docs/api/utilities/array#arrays-of-objects) instead[^1].
### Reading Data
The SheetJS [`read`](/docs/api/parse-options) function processes file data and
returns a [workbook object](/docs/csf/book). After selecting a worksheet, the
[`sheet_to_json` method](/docs/api/utilities/array#array-output) can return an
array of arrays or array of objects.
The result of `sheet_to_json` can be passed directly to the `loadData` method[^2]
of a Handsontable instance.
The following snippet fetches a file, extracts data from the first worksheet,
and passes the data to the grid:
```js
import { read, utils } from 'xlsx';
import Handsontable from 'handsontable';
/* `hot` is assumed to be the Handsontable instance */
// const hot = new Handsontable(/* ... */);
async function fetch_and_view_first_sheet() {
/* fetch and parse https://docs.sheetjs.com/pres.numbers */
const file = await (await fetch("https://docs.sheetjs.com/pres.numbers")).arrayBuffer();
const wb = read(file);
// highlight-start
/* Generate an array of arrays from the first worksheet */
const first_sheet = wb.Sheets[wb.SheetNames[0]];
const aoa = utils.sheet_to_json(first_sheet, { header: 1 });
/* load data into Handsontable instance */
hot.loadData(aoa);
// highlight-end
}
```
### Writing Data
The `getData` method[^3] of a Handsontable instance returns the current data. By
default, the method returns an array of arrays.
The SheetJS [`aoa_to_sheet`](/docs/api/utilities/array#array-of-arrays-input)
method generates a worksheet object from the data. After creating a new workbook
with [`book_new`](/docs/api/utilities/wb), [`writeFile`](/docs/api/write-options)
The following snippet pulls data from the grid and exports to `SheetJSHOT.xlsx`:
```js
import { utils, writeFile } from 'xlsx';
import Handsontable from 'handsontable';
function export_data_to_xlsx(hot) {
/* pull data from the Handsontable instance */
const aoa = hot.getData();
/* generate a SheetJS worksheet object */
const ws = utils.aoa_to_sheet(aoa);
/* generate a single-sheet workbook and export to SheetJSHOT.xlsx */
const wb = utils.book_new(ws, "Exported Data");
writeFile(wb, "SheetJSHOT.xlsx");
}
```
:::note pass
For framework-native state management, the recommended approach is to listen for
grid events and explicitly update a separate copy of the data[^4].
:::
## Demo
This sample ReactJS-powered site uses SheetJS and Handsontable to display data
from a [sample NUMBERS spreadsheet](pathname:///pres.numbers) and export data
to a new file.
1) Create a new project from the ViteJS `react-ts` template:
```bash
npm create vite@latest -- sheetjs-hot --template react-ts --no-interactive
cd sheetjs-hot
npm i
```
2) Install SheetJS and Handsontable libraries:
{`\
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz handsontable@6.2.2`}
3) Start the dev server:
```bash
npm run dev
```
The terminal window will display a URL (typically `http://localhost:5173`).
Open the URL with a web browser and confirm that a page loads.
4) Download [`App.tsx`](pathname:///handsontable/App.tsx) and replace `src/App.tsx`:
```bash
curl -L -o src/App.tsx https://docs.sheetjs.com/handsontable/App.tsx
```
#### Testing
5) Refresh the browser window. A grid and two buttons should be visible:

6) Press the "Fetch File" button. The grid will refresh with Presidential data:

7) Make some changes to the grid data.
:::note pass
Some statisticians believe President Grover Cleveland should be counted once.
That would imply President Clinton should be index 41 and the indices of the
other presidents should be decremented.
:::
Double-click on each cell in the Index column and decrement each value. The new
values should be 41, 42, 43, 44, and 45, as shown in the screenshot below:

8) Click on the "Download" button. The browser should attempt to download a new
spreadsheet (`SheetJSHOT.xlsx`). Save the file.
Open the generated file and verify the contents match the grid.
[^1]: See ["Array of Objects" in the "Data management"](https://handsontable.com/docs/javascript-data-grid/binding-to-data/#array-of-objects) section of the Handsontable documentation for more details.
[^2]: See [`loadData`](https://handsontable.com/docs/javascript-data-grid/api/core/#loaddata) in the Handsontable API documentation for more details.
[^3]: See [`getData`](https://handsontable.com/docs/javascript-data-grid/api/core/#getdata) in the Handsontable API documentation for more details.
[^4]: See ["Events and hooks" in the "Data management"](https://handsontable.com/docs/javascript-data-grid/events-and-hooks/) section of the Handsontable documentation for more details.