--- title: Electron pagination_prev: demos/mobile/index pagination_next: demos/cli/index sidebar_position: 1 sidebar_custom_props: summary: Embedded NodeJS + Chromium --- import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; The [NodeJS Module](/docs/getting-started/installation/nodejs) can be imported from the main or the renderer thread. The "Complete Example" creates an app that looks like the screenshots below:
Windows macOS Linux
![Windows screenshot](pathname:///electron/ew.png) ![macOS screenshot](pathname:///electron/em.png) ![Linux screenshot](pathname:///electron/el.png)
## Integration Details Electron presents a `fs` module. The `require('xlsx')` call loads the CommonJS module, so `XLSX.readFile` and `XLSX.writeFile` work in the renderer thread. ### Reading Files Electron offers 3 different ways to read files, two of which use Web APIs. **File Input Element** File input elements automatically map to standard Web APIs. For example, assuming a file input element on the page: ```html ``` The event handler would process the event as if it were a web event: ```js async function handleFile(e) { const file = e.target.files[0]; const data = await file.arrayBuffer(); /* data is an ArrayBuffer */ const workbook = XLSX.read(data); /* DO SOMETHING WITH workbook HERE */ } document.getElementById("xlf").addEventListener("change", handleFile, false); ``` **Drag and Drop** The [drag and drop snippet](/docs/solutions/input#example-user-submissions) applies to DIV elements on the page. For example, assuming a DIV on the page: ```html
Drop a spreadsheet file here to see sheet data
``` The event handler would process the event as if it were a web event: ```js async function handleDrop(e) { e.stopPropagation(); e.preventDefault(); const file = e.dataTransfer.files[0]; const data = await file.arrayBuffer(); /* data is an ArrayBuffer */ const workbook = XLSX.read(data); /* DO SOMETHING WITH workbook HERE */ } document.getElementById("drop").addEventListener("drop", handleDrop, false); ``` **Electron API** [`XLSX.readFile`](/docs/api/parse-options) reads workbooks from the file system. `showOpenDialog` shows a Save As dialog and returns the selected file name. Unlike the Web APIs, the `showOpenDialog` flow can be initiated by app code: ```js /* from the renderer thread */ const electron = require('@electron/remote'); /* this function will show the open dialog and try to parse the workbook */ async function importFile() { /* show Save As dialog */ const result = await electron.dialog.showOpenDialog({ title: 'Select a file', filters: [{ name: "Spreadsheets", extensions: ["xlsx", "xls", "xlsb", /* ... other formats ... */] }] }); /* result.filePaths is an array of selected files */ if(result.filePaths.length == 0) throw new Error("No file was selected!"); // highlight-next-line return XLSX.readFile(result.filePaths[0]); } ``` :::note pass `showOpenDialog` originally returned an array of paths: ```js var dialog = require('electron').remote.dialog; function importFile(workbook) { var result = dialog.showOpenDialog({ properties: ['openFile'] }); return XLSX.readFile(result[0]); } ``` This method was renamed to `showOpenDialogSync` in Electron 6. ::: ### Writing Files [`XLSX.writeFile`](/docs/api/write-options) writes workbooks to the file system. `showSaveDialog` shows a Save As dialog and returns the selected file name: ```js /* from the renderer thread */ const electron = require('@electron/remote'); /* this function will show the save dialog and try to write the workbook */ async function exportFile(workbook) { /* show Save As dialog */ const result = await electron.dialog.showSaveDialog({ title: 'Save file as', filters: [{ name: "Spreadsheets", extensions: ["xlsx", "xls", "xlsb", /* ... other formats ... */] }] }); /* write file */ // highlight-next-line XLSX.writeFile(workbook, result.filePath); } ``` :::note pass `showSaveDialog` originally returned the selected path: ```js var dialog = require('electron').remote.dialog; function exportFile(workbook) { var result = dialog.showSaveDialog(); XLSX.writeFile(workbook, result); } ``` This method was renamed to `showSaveDialogSync` in Electron 6. ::: ## Complete Example :::note Tested Deployments This demo was tested in the following environments: | OS and Version | Architecture | Electron | Date | |:---------------|:-------------|:---------|:-----------| | macOS 14.4 | `darwin-x64` | `29.1.4` | 2024-03-15 | | macOS 14.1.2 | `darwin-arm` | `27.1.3` | 2023-12-01 | | Windows 10 | `win10-x64` | `28.2.0` | 2024-03-04 | | Windows 11 | `win11-arm` | `27.1.3` | 2023-12-01 | | Linux (HoloOS) | `linux-x64` | `29.1.4` | 2024-03-21 | | Linux (Debian) | `linux-arm` | `27.1.3` | 2023-12-01 | ::: This demo includes a drag-and-drop box as well as a file input box, mirroring the [SheetJS Data Preview Live Demo](https://oss.sheetjs.com/sheetjs/) The core data in this demo is an editable HTML table. The readers build up the table using `sheet_to_html` (with `editable:true` option) and the writers scrape the table using `table_to_book`. The demo project is wired for `electron-forge` to build the standalone binary. 1) Download the demo files: - [`package.json`](pathname:///electron/package.json) : project structure - [`main.js`](pathname:///electron/main.js) : main process script - [`index.html`](pathname:///electron/index.html) : window page - [`index.js`](pathname:///electron/index.js) : script loaded in render context :::caution pass Right-click each link and select "Save Link As...". Left-clicking a link will try to load the page in your browser. The goal is to save the file contents. ::: These instructions can be run in a Terminal (bash) or Command Prompt window: ```bash mkdir sheetjs-electron cd sheetjs-electron curl -LO https://docs.sheetjs.com/electron/package.json curl -LO https://docs.sheetjs.com/electron/main.js curl -LO https://docs.sheetjs.com/electron/index.html curl -LO https://docs.sheetjs.com/electron/index.js ``` 2) Install dependencies: ```bash npm install ``` 3) To verify the app works, run in the test environment: ```bash npx -y electron . ``` The app will show. 4) To build a standalone app, run the builder: ```bash npm run make ``` This will create a package in the `out\make` folder and a standalone binary. :::caution pass On Linux, the packaging step may require additional dependencies[^1] ::: :::info pass When the demo was last tested on Windows ARM, the generated binary targeted x64. The program will run on ARM64 Windows. ::: ### Testing 5) Download [the test file `pres.numbers`](https://docs.sheetjs.com/pres.numbers) 6) Launch the generated application: | Architecture | Command | |:-------------|:--------------------------------------------------------------| | `darwin-x64` | `open ./out/sheetjs-electron-darwin-x64/sheetjs-electron.app` | | `win10-x64` | `.\out\sheetjs-electron-win32-x64\sheetjs-electron.exe` | | `linux-x64` | `./out/sheetjs-electron-linux-x64/sheetjs-electron` | #### Electron API 7) Click "Click here to select a file from your computer". With the file picker, navigate to the Downloads folder and select `pres.numbers`. The application should show data in a table. 8) Click "Export Data!" and click "Save" in the popup. By default, it will try to write to `Untitled.xls` in the Downloads folder. :::note pass In some tests, the dialog did not have a default name. If there is no default name, enter `Untitled.xls` and click "Save". ::: The app will show a popup once the data is exported. Open the file in a spreadsheet editor and compare the data to the table shown in the application. #### Drag and Drop 9) Close the application, end the terminal process and re-launch (see step 6) 10) Open the Downloads folder in a file explorer or finder window. 11) Click and drag the `pres.numbers` file from the Downloads folder to the bordered "Drop a spreadsheet file" box. The file data should be displayed. #### File Input Element 12) Close the application, end the terminal process and re-launch (see step 6) 13) Click "Choose File". With the file picker, navigate to the Downloads folder and select `pres.numbers`. ## Electron Breaking Changes The first version of this demo used Electron 1.7.5. The current demo includes the required changes for Electron 28.2.0. There are no Electron-specific workarounds in the library, but Electron broke backwards compatibility multiple times. A summary of changes is noted below. :::caution pass Electron 6 changed the return types of `dialog` API methods. The old `dialog` methods have been renamed: | Electron 1 - 5 | Electron 6 | |:-----------------|:---------------------| | `showOpenDialog` | `showOpenDialogSync` | | `showSaveDialog` | `showSaveDialogSync` | **This change was not properly documented!** Electron 9 and later require the preference `nodeIntegration: true` in order to `require('xlsx')` in the renderer process. Electron 12 and later also require `worldSafeExecuteJavascript: true` and `contextIsolation: true`. Electron 14 and later must use `@electron/remote` instead of `remote`. An `initialize` call is required to enable Developer Tools in the window. ::: [^1]: See ["Makers"](https://www.electronforge.io/config/makers) in the Electron Forge documentation. On Linux, the demo generates `rpm` and `deb` distributables. On Arch Linux and the Steam Deck, `sudo pacman -Syu rpm-tools dpkg fakeroot` installed required packages.