---
title: Spreadsheet-Powered Wails Apps
sidebar_label: Wails
description: Build data-intensive desktop apps using Wails. Seamlessly integrate spreadsheets into your app using SheetJS. Modernize Excel-powered business processes with confidence.
pagination_prev: demos/mobile/index
pagination_next: demos/cli/index
sidebar_position: 3
sidebar_custom_props:
summary: Webview + Go Backend
---
import current from '/version.js';
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeBlock from '@theme/CodeBlock';
[Wails](https://wails.io/) is a modern toolkit for building desktop apps. Wails
apps pair a Go-powered backend with a JavaScript-powered frontend[^1].
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
data from spreadsheets.
This demo uses Wails and SheetJS to pull data from a spreadsheet and display the
data in the app. We'll explore how to load SheetJS in a Wails app and exchange
file data between the JavaScript frontend and Go backend.
The ["Complete Example"](#complete-example) section covers a complete desktop
app to read and write workbooks. The app will look like the screenshots below:
Windows |
macOS |
Linux |
![Windows screenshot](pathname:///wails/win10.png)
|
![macOS screenshot](pathname:///wails/macos.png)
|
![Linux screenshot](pathname:///wails/linux.png)
|
:::tip pass
This demo assumes familiarity with the Go programming language.
For a pure JavaScript solution, the [Electron](/docs/demos/desktop/electron)
platform provides many native features out of the box.
:::
## Integration Details
The [SheetJS NodeJS Module](/docs/getting-started/installation/nodejs) can be
installed in the `frontend` folder and imported in frontend scripts.
:::caution pass
Wails currently does not provide the equivalent of NodeJS `fs` module.
**Reading and writing raw file data must be implemented in native Go code.**
:::
This demo includes native Go code for showing dialogs and reading and writing
files. When sending data between Go and JavaScript code, the raw files are
encoded as Base64 strings.
### Reading Files
When the user clicks the "Import File" button, the frontend tells the Go backend
to read data. The user will be presented with a file picker to select a file to
read. The Go backend will read the data, encode as a Base64 string, and send the
result to the frontend.
The frontend will parse the data using the SheetJS `read` method[^2], generate
HTML tables with `sheet_to_html`[^3], and display the tables on the frontend.
The following diagram summarizes the steps:
```mermaid
sequenceDiagram
actor User
participant JS as Frontend (JS)
participant Go as Backend (Go)
User->>JS: click button
JS->>Go: ask for data
Note over Go: Show Open Dialog
Note over Go: Read File Bytes
Note over Go: Generate Base64
Go->>JS: return data
Note over JS: Parse Data
`read`
Note over JS: Display Table
`sheet_to_html`
JS->>User: app shows data
```
#### Go
The Wails runtime provides the cross-platform `OpenFileDialog` function[^4] to
show a file picker. The Go standard library provides methods for reading data
from the selected file[^5] and encoding in a Base64 string[^6]
```go
import (
"context"
// highlight-start
"encoding/base64"
"os"
"github.com/wailsapp/wails/v2/pkg/runtime"
// highlight-end
)
type App struct {
ctx context.Context
}
// ReadFile shows an open file dialog and returns the data as Base64 string
func (a *App) ReadFile() string {
// highlight-next-line
selection, err := runtime.OpenFileDialog(a.ctx, runtime.OpenDialogOptions{
Title: "Select File",
Filters: []runtime.FileFilter{
{ DisplayName: "Excel Workbooks (*.xlsx)", Pattern: "*.xlsx", },
// ... more filters for more file types
},
})
if err != nil { return "" } // The demo app shows an error message
// highlight-next-line
data, err := os.ReadFile(selection)
if err != nil { return "" } // The demo app shows an error message
// highlight-next-line
return base64.StdEncoding.EncodeToString(data)
}
```
#### JS
Wails will automatically create bindings for use in JS. The `App` binding module
will export the function `ReadFile`.
The following example uses the [SvelteJS](/docs/demos/frontend/svelte) framework:
```js title="frontend/src/App.svelte"
import { read, utils } from 'xlsx';
import { ReadFile } from '../wailsjs/go/main/App';
async function importFile(evt) {
// highlight-start
/* call the native Go function and receive a base64 string */
const b64 = await ReadFile();
/* parse the base64 string with SheetJS */
const wb = read(b64, { type: "base64" });
// highlight-end
const ws = wb.Sheets[wb.SheetNames[0]]; // get the first worksheet
return utils.sheet_to_html(ws); // generate HTML table
}
```
### Writing Files
:::info pass
The SheetJS `write` method[^7] can write spreadsheets in a number of formats[^8]
including XLSX, XLSB, XLS, and NUMBERS. It expects a `bookType` option. This
means the frontend needs to know the output file name before creating the file.
:::
When the user clicks the "Export File" button, the frontend asks the Go backend
for the output filename and path. The user will be presented with a file picker
to select the output folder and workbook type. The backend will send the name
to the frontend.
The frontend will generate a workbook object from the table using the SheetJS
`table_to_book` method[^9]. The SheetJS `write` method[^10] will generate a
Base64 string from the data.
The frontend will send the Base64 string to the backend. The backend will write
the data to a file in the selected folder.
```mermaid
sequenceDiagram
actor User
participant JS as Frontend (JS)
participant Go as Backend (Go)
User->>JS: click button
JS->>Go: ask for path
Note over Go: Show Save Dialog
Go->>JS: path to save file
Note over JS: Read from Table
`table_to_book`
Note over JS: Write Workbook
`write`
JS->>Go: base64-encoded bytes
Note over Go: Decode Data
Note over Go: Write to File
Go->>JS: write finished
JS->>User: alert
```
##### Go
Two Go functions will be exposed.
- `SaveFile` will show the file picker and return the path. It will use the
cross-platform `SaveFileDialog` function[^11].
```go
import (
"context"
// highlight-next-line
"github.com/wailsapp/wails/v2/pkg/runtime"
)
type App struct {
ctx context.Context
}
func (a *App) SaveFile() string {
// highlight-next-line
selection, err := runtime.SaveFileDialog(a.ctx, runtime.SaveDialogOptions{
Title: "Select File",
DefaultFilename: "SheetJSWails.xlsx",
Filters: []runtime.FileFilter{
{ DisplayName: "Excel Workbooks (*.xlsx)", Pattern: "*.xlsx", },
// ... more filters for more file types
},
})
if err != nil { return "" } // The demo app shows an error message
return selection
}
```
- `WriteFile` performs the file write given a Base64 string and file path. The
Go standard library provides methods for decoding Base64 strings[^12] and
writing data to the filesystem[^13]
```go
import (
"context"
// highlight-start
"encoding/base64"
"os"
// highlight-end
)
type App struct {
ctx context.Context
}
func (a *App) WriteFile(b64 string, path string) {
// highlight-start
buf, _ := base64.StdEncoding.DecodeString(b64);
_ = os.WriteFile(path, buf, 0644);
// highlight-end
}
```
#### JS
Wails will automatically create bindings for use in JS. The `App` binding module
will export the functions `SaveFile` and `WriteFile`.
The following example uses the [SvelteJS](/docs/demos/frontend/svelte) framework:
```js title="frontend/src/App.svelte"
import { utils, write } from 'xlsx';
import { SaveFile, WriteFile } from '../wailsjs/go/main/App';
async function exportFile(table_element) {
/* generate workbook */
const wb = utils.table_to_book(table_element);
/* show save picker and get path */
const path = await SaveFile();
/* get the file extension -> bookType */
const bookType = path.slice(path.lastIndexOf(".")+1);
/* generate base64 string */
const b64 = write(wb, { bookType: bookType, type: "base64" });
/* write to file */
await WriteFile(b64, path);
}
```
## Complete Example
:::note Tested Deployments
This demo was tested in the following environments:
| OS and Version | Architecture | Wails | Date |
|:---------------|:-------------|:---------|:-----------|
| macOS 14.4 | `darwin-x64` | `v2.8.0` | 2024-03-15 |
| macOS 14.1.2 | `darwin-arm` | `v2.6.0` | 2023-12-01 |
| Windows 10 | `win10-x64` | `v2.8.0` | 2024-03-24 |
| Windows 11 | `win11-arm` | `v2.6.0` | 2023-12-01 |
| Linux (HoloOS) | `linux-x64` | `v2.8.0` | 2024-03-21 |
| Linux (Debian) | `linux-arm` | `v2.6.0` | 2023-12-01 |
:::
0) Read the Wails "Getting Started" guide[^14] and install dependencies.
Installation Notes (click to show)
Wails will require:
- A recent version of [Go](https://go.dev/doc/install).
- The "LTS" version of [NodeJS](https://nodejs.org/en/download).
After installing both, run the following command to install Wails:
```bash
go install github.com/wailsapp/wails/v2/cmd/wails@latest
```
Once that finishes, run the following command in a new terminal window:
```bash
wails doctor
```
:::note pass
On macOS and Linux, the `PATH` environment variable must include `~/go/bin`. If
`wails` cannot be found, run the following command in the terminal session:
```bash
export PATH="$PATH:~/go/bin"
```
:::
The output will include a `# Diagnosis` section. It should display:
```
SUCCESS Your system is ready for Wails development!
```
If a required dependency is missing, it will be displayed.
:::note pass
None of the optional packages are required for building and running this demo.
:::
:::info pass
On the Steam Deck (HoloOS), some dependencies must be reinstalled:
```bash
sudo pacman -Syu base-devel gtk3 glib2 pango harfbuzz cairo gdk-pixbuf2 atk libsoup
```
:::
1) Create a new Wails app:
```bash
wails init -n sheetjs-wails -t svelte-ts
cd sheetjs-wails
```
2) Install front-end dependencies:
{`\
cd frontend
curl -o src/assets/logo.png https://sheetjs.com/sketch1024.png
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz
cd ..`}
3) Download source files:
- Download [`app.go`](pathname:///wails/app.go) and replace `app.go`
- Download [`App.svelte`](pathname:///wails/App.svelte) and replace
`frontend/src/App.svelte`
```bash
curl -o app.go https://docs.sheetjs.com/wails/app.go
curl -o frontend/src/App.svelte https://docs.sheetjs.com/wails/App.svelte
```
4) Build the app:
```bash
wails build
```
It will print the path to the generated program (typically in `build/bin/`).
5) Run the generated application.
**Testing**
The program will download [`pres.xlsx`](https://docs.sheetjs.com/pres.xlsx) and
display the contents of the first worksheet in a table.
To test export features, click "Export XLSX". The app will ask for a file name
and location. After clicking Save, the app will export to XLSX. This file can be
opened in a spreadsheet editor such as Excel.
[^1]: See ["How does it Work?"](https://wails.io/docs/howdoesitwork) in the Wails documentation.
[^2]: See [`read` in "Reading Files"](/docs/api/parse-options)
[^3]: See [`sheet_to_html` in "Utilities"](/docs/api/utilities/html#html-table-output)
[^4]: See [`OpenFileDialog`](https://wails.io/docs/reference/runtime/dialog#openfiledialog) in the Wails documentation.
[^5]: See [`ReadFile`](https://pkg.go.dev/os#ReadFile) in the Go documentation
[^6]: See [`EncodeToString`](https://pkg.go.dev/encoding/base64#Encoding.EncodeToString) in the Go documentation
[^7]: See [`write` in "Writing Files"](/docs/api/write-options)
[^8]: See ["Supported Output Formats" type in "Writing Files"](/docs/api/write-options#supported-output-formats)
[^9]: See ["HTML Table Input" in "Utilities"](/docs/api/utilities/html#create-new-sheet)
[^10]: See [`write` in "Writing Files"](/docs/api/write-options)
[^11]: See [`SaveFileDialog`](https://wails.io/docs/reference/runtime/dialog#savefiledialog) in the Wails documentation.
[^12]: See [`DecodeString`](https://pkg.go.dev/encoding/base64#Encoding.DecodeString) in the Go documentation
[^13]: See [`WriteFile`](https://pkg.go.dev/os#WriteFile) in the Go documentation
[^14]: See ["Installation"](https://wails.io/docs/gettingstarted/installation) in the Wails documentation.