Skip to main content

Data Wrangling in Tauri Apps

Tauri is a modern toolkit for building desktop apps. Tauri apps leverage platform-native browser engines to build lightweight programs.

SheetJS is a JavaScript library for reading and writing data from spreadsheets.

This demo uses Tauri and SheetJS to pull data from a spreadsheet and display the data in the app. We'll explore how to load SheetJS in a Tauri app and exchange file data between the JavaScript frontend and Rust backend.

The "Complete Example" section covers a complete desktop app to read and write workbooks. The app will look like the screenshots below:

WindowsmacOSLinux

Windows screenshot

macOS screenshot

Linux screenshot

Integration Details

The SheetJS NodeJS Module can be installed and imported from JavaScript code.

Tauri currently does not provide the equivalent of NodeJS fs module. The raw @tauri-apps/api methods used in the examples are not expected to change.

For security reasons, Tauri apps must explicitly enable system features.1 They are enabled in src-tauri/tauri.conf.json in the allowlist subsection of the tauri section of the config.

  • The fs entitlement2 enables reading and writing file data.
src-tauri/tauri.conf.json
  "tauri": {
"allowlist": {
"fs": {
"all": true
}
  • The dialog entitlement3 enables the open and save dialog methods.
src-tauri/tauri.conf.json
  "tauri": {
"allowlist": {
"dialog": {
"all": true
}
  • The http entitlement4 enables downloading files. Note that http is not needed for reading or writing files in the local filesystem.
src-tauri/tauri.conf.json
  "tauri": {
"allowlist": {
"http": {
"all": true,
"request": true,
"scope": ["https://**"]
}

Reading Files

There are three steps to reading files:

  1. Show an open file dialog to allow users to select a path. The open method in @tauri-apps/api/dialog5 simplifies this process.

  2. Read raw data from the selected file using the readBinaryFile method in @tauri-apps/api/fs6. This method resolves to a standard Uint8Array

  3. Parse the data with the SheetJS read method7. This method returns a SheetJS workbook object.

The following code example defines a single function openFile that performs all three steps and returns a SheetJS workbook object:

import { read } from 'xlsx';
import { open } from '@tauri-apps/api/dialog';
import { readBinaryFile } from '@tauri-apps/api/fs';

const filters = [
{name: "Excel Binary Workbook", extensions: ["xlsb"]},
{name: "Excel Workbook", extensions: ["xlsx"]},
{name: "Excel 97-2004 Workbook", extensions: ["xls"]},
// ... other desired formats ...
];

async function openFile() {
/* show open file dialog */
const selected = await open({
title: "Open Spreadsheet",
multiple: false,
directory: false,
filters
});

/* read data into a Uint8Array */
const d = await readBinaryFile(selected);

/* parse with SheetJS */
const wb = read(d);
return wb;
}

At this point, standard SheetJS utility functions8 can extract data from the workbook object. The demo includes a button that calls sheet_to_json9 to generate an array of arrays of data.

The following snippet shows a simple Kaioponent:

Kaioponent for importing data
import { utils } from 'xlsx';
import { useState } from 'kaioken';

function SheetJSImportKaioponent() {
const [data, setData] = useState<any[][]>([]);

const open_callback = async() => {
const wb = await openFile();

/* get the first worksheet */
const ws = wb.Sheets[wb.SheetNames[0]];

/* get data from the first worksheet */
const array = utils.sheet_to_json(ws, { header: 1 });
setData(array);
};

return ( <>
<button type="button" onclick={open_callback}>Load Data</button>
<table><tbody>{data.map((row) =>
<tr>{row.map((cell) => <td>{cell}</td>)}</tr>
)}</tbody></table>
</>);
}

Writing Files

There are three steps to writing files:

  1. Show a save file dialog to allow users to select a path. The save method in @tauri-apps/api/dialog10 simplifies this process.

  2. Write the data with the SheetJS write method11. The output book type can be inferred from the selected file path. Using the buffer output type12, the method will return a Uint8Array object that plays nice with Tauri.

  3. Write the data using writeBinaryFile in @tauri-apps/api/fs13.

The following code example defines a single function saveFile that performs all three steps starting from a SheetJS workbook object:

import { write } from 'xlsx';
import { save } from '@tauri-apps/api/dialog';
import { writeBinaryFile } from '@tauri-apps/api/fs';

const filters = [
{name: "Excel Binary Workbook", extensions: ["xlsb"]},
{name: "Excel Workbook", extensions: ["xlsx"]},
{name: "Excel 97-2004 Workbook", extensions: ["xls"]},
// ... other desired formats ...
];

async function saveFile(wb) {
/* show save file dialog */
const selected = await save({
title: "Save to Spreadsheet",
filters
});
if(!selected) return;

/* Generate workbook */
const bookType = selected.slice(selected.lastIndexOf(".") + 1);
const d = write(wb, {type: "buffer", bookType});

/* save data to file */
await writeBinaryFile(selected, d);
}

The demo includes a button that calls aoa_to_sheet14 to generate a sheet from array of arrays of data. A workbook is constructed using book_new and book_append_sheet15.

The following snippet shows a simple Kaioponent:

Kaioponent for exporting data
import { utils } from 'xlsx';
import { useState } from 'kaioken';

function SheetJSExportKaioponent() {
const [data, setData] = useState<any[][]>(["SheetJS".split(""), "Kaioken".split("")]);

const save_callback = async() => {
/* generate worksheet from the data */
const ws = utils.aoa_to_sheet(data);

/* create a new workbook object */
const wb = utils.book_new();

/* append the worksheet to the workbook using the sheet name "SheetJSTauri" */
utils.book_append_sheet(wb, ws, "SheetJSTauri");

await saveFile(wb);
}

return ( <button type="button" onclick={save_callback}>Save Data</button> );
}

Complete Example

Tested Deployments

This demo was tested in the following environments:

OS and VersionArchitectureTauriDate
macOS 14.4darwin-x64v1.5.112024-03-15
macOS 14.0darwin-armv1.5.22023-10-18
Windows 10win10-x64v1.5.112024-03-24
Windows 11win11-armv1.5.72023-12-01
Linux (HoloOS)linux-x64v1.5.112024-03-21
Linux (Debian)linux-armv1.5.72023-12-01
  1. Read Tauri "Getting Started" guide and install prerequisites.16
Installation Notes (click to show)

At a high level, the following software is required for building Tauri apps:

  • a native platform-specific C/C++ compiler (for example, macOS requires Xcode)
  • a browser engine integration (for example, linux requires webkit2gtk)
  • Rust

The platform configuration can be verified by running:

npx @tauri-apps/cli info

If required dependencies are installed, the output will show a checkmark next to "Environment". The output from the most recent macOS test is shown below:

[✔] Environment
- OS: Mac OS 14.4.0 X64
✔ Xcode Command Line Tools: installed
✔ rustc: 1.76.0 (07dca489a 2024-02-04)
✔ cargo: 1.76.0 (c84b36747 2024-01-18)
✔ rustup: 1.27.0 (bbb9276d2 2024-03-08)
✔ Rust toolchain: stable-x86_64-apple-darwin (default)
- node: 20.11.1
- npm: 10.2.4
- bun: 1.0.31

When the demo was last tested on ARM64 macOS, the output mentioned X64. The build step will correctly detect the platform architecture.

  1. Create a new Tauri app:

There is no official Tauri Kaioken template. This demo starts from the vanilla TypeScript template and manually wires Kaioken

npm create tauri-app@latest -- -m npm -t vanilla-ts SheetJSTauri -y
  1. Enter the directory and install dependencies:
cd SheetJSTauri
npm i --save https://cdn.sheetjs.com/xlsx-0.20.2/xlsx-0.20.2.tgz
npm i --save @tauri-apps/api
npm i --save-dev @tauri-apps/cli
npm add kaioken --save
npm add vite-plugin-kaioken -D --save
  1. Add the highlighted lines to src-tauri/tauri.conf.json in the tauri.allowlist section:
src-tauri/tauri.conf.json (add highlighted lines)
  "tauri": {
"allowlist": {
"http": {
"all": true,
"request": true,
"scope": ["https://**"]
},
"dialog": {
"all": true
},
"fs": {
"all": true
},

In the same file, look for the "identifier" key and replace the value with com.sheetjs.tauri:

src-tauri/tauri.conf.json (edit highlighted line)
        "icons/icon.ico"
],
"identifier": "com.sheetjs.tauri",
"longDescription": "",
  1. Wire up Kaioken to the Tauri app:
  • Add the highlighted lines to vite.config.ts:
vite.config.ts (add highlighted lines)
import { defineConfig } from "vite";
import kaioken from "vite-plugin-kaioken"

// https://vitejs.dev/config/
export default defineConfig(async () => ({
esbuild: {
jsxInject: `import * as kaioken from "kaioken"`,
jsx: "transform",
jsxFactory: "kaioken.createElement",
jsxFragment: "kaioken.fragment",
loader: "tsx",
include: ["**/*.tsx", "**/*.ts", "**/*.jsx", "**/*.js"],
},
plugins: [kaioken()],
  • Add the highlighted line to tsconfig.json:
tsconfig.json (add highlighted line)
{
"compilerOptions": {
"jsx": "preserve",
"target": "ES2020",
  • Replace index.html with the following codeblock:
index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="stylesheet" href="/src/styles.css" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>SheetJS x Tauri</title>
<script type="module" src="/src/main.ts" defer></script>
</head>

<body>
<div id="container" class="container"></div>
</body>
</html>
  • Add the following lines to src/styles.css:
src/styles.css (add to end)
.logo {
padding: 0px;
height: 64px; width: 64px;
vertical-align: middle;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.centre { text-align: center; }
table.center {
margin-left: auto;
margin-right: auto;
}
  • Replace src/main.ts with the following codeblock:
src/main.ts
import { mount } from "kaioken";
import App from "./App";

const root = document.getElementById("container");
mount(App, root!);
  • Download App.tsx and save to src/App.tsx:
curl -o src/App.tsx https://docs.sheetjs.com/tauri/App.tsx
  1. Build the app with
npm run tauri build

At the end, it will print the path to the generated installer.

If the build fails, see "Troubleshooting" for more details.

  1. Run the program.

Depending on the version of Tauri, the command may be

./src-tauri/target/release/SheetJSTauri

or

./src-tauri/target/release/sheet-js-tauri

or

./src-tauri/target/release/sheetjstauri

The following features should be manually verified:

  • When it is loaded, the app will download https://sheetjs.com/pres.numbers and display the data in a table.
  • Clicking "Save Data" will show a save dialog. After selecting a path and name, the app will write a file. That file can be opened in a spreadsheet editor.
  • Edit the file in a spreadsheet editor, then click "Load Data" and select the edited file. The table will refresh with new contents.

Troubleshooting

During the last Linux ARM64 test, the build failed to create an AppImage:

        Error [tauri-cli-node] failed to bundle project: error running appimage.sh

This is a known Tauri AppImage packaging bug. Since the actual application and the .deb distributable are created, the error can be ignored.

During the last Linux x64 test, the build failed with the error message:

'openssl/opensslv.h' file not found

OpenSSL must be installed. On Arch Linux and HoloOS (Steam Deck):

sudo pacman -S openssl

During the last macOS test, the build failed with the following error message:

       Error failed to bundle project: error running bundle_dmg.sh

The root cause of the error can be discovered by running

npm run tauri build -- --verbose

The most recent test failed with a message:

execution error: Not authorized to send Apple events to Finder

This error was resolved by allowing Terminal to control Finder.

In the "System Settings" app, select "Privacy & Security" in the left column and select "Automation" in the body. Look for "Terminal", expand the section, and enable "Finder".

Footnotes

  1. See "Security" in the Tauri documentation

  2. See FsAllowlistConfig in the Tauri documentation

  3. See DialogAllowlistConfig in the Tauri documentation

  4. See HttpAllowlistConfig in the Tauri documentation

  5. See dialog in the Tauri documentation

  6. See fs in the Tauri documentation

  7. See read in "Reading Files"

  8. See "Utility Functions"

  9. See "Array Output" in "Utility Functions"

  10. See dialog in the Tauri documentation

  11. See write in "Writing Files"

  12. See "Supported Output Formats"

  13. See fs in the Tauri documentation

  14. See "Array of Arrays Input" in "Utility Functions"

  15. See "Workbook Helpers" in "Utility Functions"

  16. See "Prerequisites" in the Tauri documentation