228 lines
7.2 KiB
Markdown
228 lines
7.2 KiB
Markdown
|
|
---
|
||
|
|
title: Handsontable
|
||
|
|
pagination_prev: demos/frontend/index
|
||
|
|
pagination_next: demos/net/index
|
||
|
|
---
|
||
|
|
|
||
|
|
<head>
|
||
|
|
<script src="https://cdn.jsdelivr.net/npm/handsontable@6.2.2/dist/handsontable.full.js"></script>
|
||
|
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/handsontable@6.2.2/dist/handsontable.full.css"/>
|
||
|
|
</head>
|
||
|
|
|
||
|
|
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:
|
||
|
|
|
||
|
|
<CodeBlock language="bash">{`\
|
||
|
|
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz handsontable@6.2.2`}
|
||
|
|
</CodeBlock>
|
||
|
|
|
||
|
|
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:
|
||
|
|
|
||
|
|
<CodeBlock language="bash">{`\
|
||
|
|
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz handsontable@6.2.2`}
|
||
|
|
</CodeBlock>
|
||
|
|
|
||
|
|
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.
|