Spreadsheet-Powered Wails Apps
Wails is a modern toolkit for building desktop apps. Wails apps pair a Go-powered backend with a JavaScript-powered frontend1.
SheetJS 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" section covers a complete desktop app to read and write workbooks. The app will look like the screenshots below:
| Windows | macOS | Linux |
|---|---|---|
|
|
|
This demo assumes familiarity with the Go programming language.
For a pure JavaScript solution, the Electron platform provides many native features out of the box.
Integration Details​
The SheetJS NodeJS Module can be
installed in the frontend folder and imported in frontend scripts.
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 method2, generate
HTML tables with sheet_to_html3, and display the tables on the frontend.
The following diagram summarizes the steps:
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<br/>`read`
Note over JS: Display Table<br/>`sheet_to_html`
JS->>User: app shows data
Go​
The Wails runtime provides the cross-platform OpenFileDialog function4 to
show a file picker. The Go standard library provides methods for reading data
from the selected file5 and encoding in a Base64 string6
import (
"context"
"encoding/base64"
"os"
"github.com/wailsapp/wails/v2/pkg/runtime"
)
type App struct {
ctx context.Context
}
// ReadFile shows an open file dialog and returns the data as Base64 string
func (a *App) ReadFile() string {
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
data, err := os.ReadFile(selection)
if err != nil { return "" } // The demo app shows an error message
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 framework:
import { read, utils } from 'xlsx';
import { ReadFile } from '../wailsjs/go/main/App';
async function importFile(evt) {
/* 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" });
const ws = wb.Sheets[wb.SheetNames[0]]; // get the first worksheet
return utils.sheet_to_html(ws); // generate HTML table
}
Writing Files​
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 method9. The SheetJS write method10 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.
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<br/>`table_to_book`
Note over JS: Write Workbook<br/>`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.
SaveFilewill show the file picker and return the path. It will use the cross-platformSaveFileDialogfunction11.
import (
"context"
"github.com/wailsapp/wails/v2/pkg/runtime"
)
type App struct {
ctx context.Context
}
func (a *App) SaveFile() string {
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
}
WriteFileperforms the file write given a Base64 string and file path. The Go standard library provides methods for decoding Base64 strings12 and writing data to the filesystem13
import (
"context"
"encoding/base64"
"os"
)
type App struct {
ctx context.Context
}
func (a *App) WriteFile(b64 string, path string) {
buf, _ := base64.StdEncoding.DecodeString(b64);
_ = os.WriteFile(path, buf, 0644);
}
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 framework:
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​
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.5 | darwin-arm | v2.8.2 | 2024-05-28 |
| Windows 10 | win10-x64 | v2.8.0 | 2024-03-24 |
| Windows 11 | win11-arm | v2.8.2 | 2024-05-28 |
| Linux (HoloOS) | linux-x64 | v2.8.0 | 2024-03-21 |
| Linux (Debian) | linux-arm | v2.8.2 | 2024-05-28 |
- Read the Wails "Getting Started" guide14 and install dependencies.
Installation Notes (click to show)
- Create a new Wails app:
wails init -n sheetjs-wails -t svelte-ts
cd sheetjs-wails
- 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-0.20.3/xlsx-0.20.3.tgz
cd ..
- Download source files:
- Download
app.goand replaceapp.go - Download
App.svelteand replacefrontend/src/App.svelte
curl -o app.go https://docs.sheetjs.com/wails/app.go
curl -o frontend/src/App.svelte https://docs.sheetjs.com/wails/App.svelte
- Build the app:
wails build
It will print the path to the generated program (typically in build/bin/).
- Run the generated application.
Testing
The program will download 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.
Footnotes​
-
See "How does it Work?" in the Wails documentation. ↩
-
See
OpenFileDialogin the Wails documentation. ↩ -
See
EncodeToStringin the Go documentation ↩ -
See
SaveFileDialogin the Wails documentation. ↩ -
See
DecodeStringin the Go documentation ↩ -
See "Installation" in the Wails documentation. ↩


