Pular para o conteúdo

Wails Cheat Sheet

Overview

Wails is a framework for building desktop applications using Go and web technologies. Unlike Electron, which bundles a full Chromium browser, Wails uses the native webview component of each operating system (WebKit on macOS, WebView2 on Windows, WebKitGTK on Linux), resulting in significantly smaller binary sizes (typically 5-10MB vs 150MB+ for Electron) and lower memory usage. Your Go code runs natively, and the frontend communicates with it through auto-generated TypeScript bindings.

Created by Lea Anthony, Wails v2 provides a production-ready framework for building cross-platform desktop applications. It supports any frontend framework (React, Vue, Svelte, etc.) or plain HTML/CSS/JS. Go methods can be bound to the frontend and called directly from JavaScript with full type safety. Wails also provides native OS integration including menus, dialogs, system tray, notifications, and window management.

Installation

Prerequisites

# Go 1.21+ required
go version

# Install Wails CLI
go install github.com/wailsapp/wails/v2/cmd/wails@latest

# Check system dependencies
wails doctor

Platform Dependencies

# macOS - Xcode command line tools
xcode-select --install

# Ubuntu/Debian
sudo apt install libgtk-3-dev libwebkit2gtk-4.0-dev

# Fedora
sudo dnf install gtk3-devel webkit2gtk4.0-devel

# Arch
sudo pacman -S webkit2gtk gtk3

# Windows - WebView2 Runtime (usually pre-installed on Windows 10/11)
# If missing: https://developer.microsoft.com/en-us/microsoft-edge/webview2/

Creating a Project

Project Templates

# Default (vanilla JS)
wails init -n myapp

# With React
wails init -n myapp -t react-ts

# With Vue
wails init -n myapp -t vue-ts

# With Svelte
wails init -n myapp -t svelte-ts

# With Preact
wails init -n myapp -t preact-ts

# List all templates
wails init -l

Project Structure

PathDescription
main.goApplication entry point
app.goApplication struct with bound methods
wails.jsonWails project configuration
frontend/Frontend web application
frontend/src/Frontend source code
frontend/wailsjs/Auto-generated Go bindings
build/Build artifacts
build/appicon.pngApplication icon

Development

Dev Commands

CommandDescription
wails devStart dev mode with hot reload
wails buildBuild production binary
wails build -o myappBuild with custom output name
wails build -platform windowsCross-compile for Windows
wails build -platform darwinBuild for macOS
wails build -platform linuxBuild for Linux
wails build -nsisBuild Windows installer (NSIS)
wails build -upxCompress binary with UPX
wails doctorCheck system dependencies
wails generate moduleRegenerate bindings
# Dev mode with browser devtools
wails dev

# Dev mode opening in browser (for browser devtools)
wails dev -browser

# Build optimized production binary
wails build -clean -trimpath -ldflags "-s -w"

Backend (Go)

Application Structure

// main.go
package main

import (
    "embed"
    "github.com/wailsapp/wails/v2"
    "github.com/wailsapp/wails/v2/pkg/options"
    "github.com/wailsapp/wails/v2/pkg/options/assetserver"
    "github.com/wailsapp/wails/v2/pkg/options/linux"
    "github.com/wailsapp/wails/v2/pkg/options/mac"
    "github.com/wailsapp/wails/v2/pkg/options/windows"
)

//go:embed all:frontend/dist
var assets embed.FS

func main() {
    app := NewApp()

    err := wails.Run(&options.App{
        Title:     "My App",
        Width:     1024,
        Height:    768,
        MinWidth:  800,
        MinHeight: 600,
        AssetServer: &assetserver.Options{
            Assets: assets,
        },
        BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 1},
        OnStartup:        app.startup,
        OnShutdown:       app.shutdown,
        OnDomReady:       app.domReady,
        Bind: []interface{}{
            app,
        },
        Mac: &mac.Options{
            TitleBar: &mac.TitleBar{
                TitlebarAppearsTransparent: true,
                HideTitle:                 false,
                HideTitleBar:              false,
                FullSizeContent:           true,
            },
        },
        Windows: &windows.Options{
            WebviewIsTransparent: false,
            WindowIsTranslucent:  false,
        },
        Linux: &linux.Options{
            ProgramName: "My App",
        },
    })

    if err != nil {
        println("Error:", err.Error())
    }
}

Binding Go Methods

// app.go
package main

import (
    "context"
    "fmt"
    "time"
    "github.com/wailsapp/wails/v2/pkg/runtime"
)

type App struct {
    ctx context.Context
}

func NewApp() *App {
    return &App{}
}

func (a *App) startup(ctx context.Context) {
    a.ctx = ctx
}

func (a *App) shutdown(ctx context.Context) {
    // Cleanup
}

func (a *App) domReady(ctx context.Context) {
    // DOM is ready
}

// Exported methods are callable from frontend
func (a *App) Greet(name string) string {
    return fmt.Sprintf("Hello %s, it's %s!", name, time.Now().Format("3:04 PM"))
}

// Struct types work too
type User struct {
    Name  string `json:"name"`
    Email string `json:"email"`
    Age   int    `json:"age"`
}

func (a *App) GetUser(id int) (*User, error) {
    // Fetch from database etc.
    return &User{
        Name:  "Alice",
        Email: "alice@example.com",
        Age:   30,
    }, nil
}

func (a *App) SaveUser(user User) error {
    // Save to database
    fmt.Printf("Saving user: %+v\n", user)
    return nil
}

Runtime APIs

// Dialogs
result, err := runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
    Type:    runtime.QuestionDialog,
    Title:   "Confirm",
    Message: "Are you sure?",
    Buttons: []string{"Yes", "No"},
})

// File dialogs
filepath, err := runtime.OpenFileDialog(a.ctx, runtime.OpenDialogOptions{
    Title: "Select File",
    Filters: []runtime.FileFilter{
        {DisplayName: "Images", Pattern: "*.png;*.jpg;*.jpeg"},
        {DisplayName: "All Files", Pattern: "*.*"},
    },
})

savePath, err := runtime.SaveFileDialog(a.ctx, runtime.SaveDialogOptions{
    Title:           "Save As",
    DefaultFilename: "export.json",
})

// Window control
runtime.WindowSetTitle(a.ctx, "New Title")
runtime.WindowSetSize(a.ctx, 1280, 720)
runtime.WindowCenter(a.ctx)
runtime.WindowMinimise(a.ctx)
runtime.WindowMaximise(a.ctx)
runtime.WindowToggleMaximise(a.ctx)
runtime.WindowFullscreen(a.ctx)
runtime.WindowUnfullscreen(a.ctx)
runtime.WindowHide(a.ctx)
runtime.WindowShow(a.ctx)

// Events (Go -> Frontend)
runtime.EventsEmit(a.ctx, "data-updated", data)

// Events (Frontend -> Go listener)
runtime.EventsOn(a.ctx, "frontend-event", func(data ...interface{}) {
    fmt.Println("Received:", data)
})

// Clipboard
runtime.ClipboardSetText(a.ctx, "copied text")
text, _ := runtime.ClipboardGetText(a.ctx)

// Environment
env := runtime.Environment(a.ctx)
fmt.Println(env.Platform) // "darwin", "windows", "linux"

Frontend Integration

Calling Go from JavaScript/TypeScript

// Auto-generated bindings in frontend/wailsjs/go/main/App.ts
import { Greet, GetUser, SaveUser } from '../wailsjs/go/main/App';
import { main } from '../wailsjs/go/models';

// Call Go methods
async function greet() {
  const result = await Greet("World");
  console.log(result); // "Hello World, it's 3:04 PM!"
}

// With typed models
async function loadUser() {
  const user: main.User = await GetUser(1);
  console.log(user.name, user.email);
}

// Send data to Go
async function saveUser() {
  const user = new main.User();
  user.name = "Bob";
  user.email = "bob@example.com";
  user.age = 25;
  await SaveUser(user);
}

Wails Runtime (Frontend)

import { EventsOn, EventsEmit, EventsOff } from '../wailsjs/runtime/runtime';

// Listen for Go events
EventsOn("data-updated", (data: any) => {
  console.log("Data updated:", data);
});

// Emit events to Go
EventsEmit("frontend-event", { action: "click", target: "button" });

// Cleanup listener
EventsOff("data-updated");

// Window drag (for frameless windows)
// Add wails-drag attribute to HTML element:
// <div data-wails-drag>Drag me</div>

React Example

import { useState, useEffect } from 'react';
import { Greet, GetUser } from '../wailsjs/go/main/App';
import { EventsOn } from '../wailsjs/runtime/runtime';

function App() {
  const [greeting, setGreeting] = useState('');
  const [name, setName] = useState('');

  useEffect(() => {
    const cleanup = EventsOn("notification", (msg: string) => {
      alert(msg);
    });
    return () => cleanup();
  }, []);

  const handleGreet = async () => {
    const result = await Greet(name);
    setGreeting(result);
  };

  return (
    <div>
      <input value={name} onChange={(e) => setName(e.target.value)} />
      <button onClick={handleGreet}>Greet</button>
      <p>{greeting}</p>
    </div>
  );
}

Configuration

wails.json

{
  "$schema": "https://wails.io/schemas/config.v2.json",
  "name": "myapp",
  "outputfilename": "myapp",
  "frontend:install": "npm install",
  "frontend:build": "npm run build",
  "frontend:dev:watcher": "npm run dev",
  "frontend:dev:serverUrl": "auto",
  "author": {
    "name": "Developer",
    "email": "dev@example.com"
  },
  "info": {
    "companyName": "My Company",
    "productName": "My App",
    "productVersion": "1.0.0",
    "copyright": "Copyright 2024",
    "comments": "Built with Wails"
  }
}

Advanced Usage

import "github.com/wailsapp/wails/v2/pkg/menu"
import "github.com/wailsapp/wails/v2/pkg/menu/keys"

func (a *App) createMenu() *menu.Menu {
    appMenu := menu.NewMenu()

    fileMenu := appMenu.AddSubmenu("File")
    fileMenu.AddText("Open", keys.CmdOrCtrl("o"), func(cd *menu.CallbackData) {
        // Handle open
    })
    fileMenu.AddSeparator()
    fileMenu.AddText("Quit", keys.CmdOrCtrl("q"), func(cd *menu.CallbackData) {
        runtime.Quit(a.ctx)
    })

    return appMenu
}

System Tray

// Wails v3 (upcoming) has native system tray support
// For v2, use third-party packages like systray

Troubleshooting

ProblemSolution
wails doctor failsInstall missing platform dependencies listed in output
Bindings not generatedRun wails generate module; check Go methods are exported
White screen in devCheck frontend dev server is running; verify URL in console
Build fails on LinuxInstall libgtk-3-dev and libwebkit2gtk-4.0-dev
Window not showingCheck Width/Height in options; verify OnStartup doesn’t panic
Cross-compile failsUse Docker or CI for cross-platform builds
Large binary sizeUse -trimpath -ldflags "-s -w" and -upx flags
Hot reload not workingEnsure frontend:dev:watcher is correct in wails.json