What is Wails?
Wails lets you build desktop applications using Go for the backend and any web framework for the frontend. Think Electron, but with Go instead of Node.js — resulting in binaries that are 10-50x smaller and use significantly less memory.
Quick Start
go install github.com/wailsapp/wails/v2/cmd/wails@latest
wails init -n myapp -t react-ts
cd myapp
wails dev
You get a native desktop app with React + TypeScript frontend and Go backend, with hot reload.
How It Works
Wails binds Go functions directly to JavaScript — no REST API, no WebSocket, no IPC boilerplate:
// app.go
package main
import "context"
type App struct {
ctx context.Context
}
func NewApp() *App {
return &App{}
}
func (a *App) startup(ctx context.Context) {
a.ctx = ctx
}
// This function is callable from JavaScript!
func (a *App) Greet(name string) string {
return "Hello " + name + "!"
}
// Complex types work too
type FileInfo struct {
Name string `json:"name"`
Size int64 `json:"size"`
Path string `json:"path"`
}
func (a *App) ListFiles(dir string) ([]FileInfo, error) {
entries, err := os.ReadDir(dir)
if err != nil {
return nil, err
}
var files []FileInfo
for _, e := range entries {
info, _ := e.Info()
files = append(files, FileInfo{
Name: e.Name(),
Size: info.Size(),
Path: filepath.Join(dir, e.Name()),
})
}
return files, nil
}
Call Go from React
Wails auto-generates TypeScript bindings:
// frontend/src/App.tsx
import { Greet, ListFiles } from "../wailsjs/go/main/App";
function App() {
const [greeting, setGreeting] = useState("");
const [files, setFiles] = useState([]);
const handleGreet = async () => {
const result = await Greet("World");
setGreeting(result); // "Hello World!"
};
const handleListFiles = async () => {
const result = await ListFiles("/home/user");
setFiles(result); // [{name, size, path}, ...]
};
return (
<div>
<button onClick={handleGreet}>Greet</button>
<p>{greeting}</p>
<button onClick={handleListFiles}>List Files</button>
{files.map(f => <div key={f.path}>{f.name} ({f.size} bytes)</div>)}
</div>
);
}
No fetch calls. No API routes. Direct function calls with full type safety.
Events System
// Go: emit event
runtime.EventsEmit(a.ctx, "file-processed", filename, progress)
// Go: listen for event
runtime.EventsOn(a.ctx, "user-action", func(data ...interface{}) {
fmt.Println("User did:", data)
})
// Frontend: listen for event
import { EventsOn } from "../wailsjs/runtime";
EventsOn("file-processed", (filename, progress) => {
console.log(`${filename}: ${progress}%`);
});
Native Dialogs
import "github.com/wailsapp/wails/v2/pkg/runtime"
func (a *App) OpenFile() (string, error) {
return runtime.OpenFileDialog(a.ctx, runtime.OpenDialogOptions{
Title: "Select File",
Filters: []runtime.FileFilter{
{DisplayName: "Images", Pattern: "*.png;*.jpg;*.gif"},
},
})
}
func (a *App) SaveFile(content string) error {
path, _ := runtime.SaveFileDialog(a.ctx, runtime.SaveDialogOptions{
Title: "Save File",
DefaultFilename: "output.txt",
})
return os.WriteFile(path, []byte(content), 0644)
}
Build for All Platforms
# Build for current platform
wails build
# Build with NSIS installer (Windows)
wails build -nsis
# Cross-compile
wails build -platform darwin/amd64
wails build -platform windows/amd64
wails build -platform linux/amd64
Wails vs Electron
| Metric | Wails | Electron |
|---|---|---|
| Binary Size | 8-15 MB | 150-250 MB |
| Memory Usage | 30-50 MB | 200-500 MB |
| Startup Time | <1s | 2-5s |
| Backend | Go | Node.js |
| Frontend | Any web framework | Any web framework |
Need a lightweight desktop app or Go-powered developer tool?
📧 spinov001@gmail.com
🔧 My tools on Apify Store
Go + web frontend — is this the future of desktop apps? Let me know!
Top comments (0)