DEV Community

Alex Spinov
Alex Spinov

Posted on

Wails Has a Free API: Build Desktop Apps With Go and Web Technologies

Wails lets you build cross-platform desktop applications using Go for the backend and any web framework for the frontend — no Electron, no embedded browser, no massive bundle sizes.

Why Wails Matters

Electron apps ship a full Chromium browser (150MB+). Wails uses the OS native webview (WebKit on macOS, WebView2 on Windows, WebKitGTK on Linux), producing binaries under 10MB.

What you get for free:

  • Native OS webview — no bundled Chromium
  • Go backend with automatic TypeScript bindings
  • Hot reload for both Go and frontend code
  • Native menus, dialogs, system tray
  • Cross-compile for Windows, macOS, Linux from any OS
  • App binaries typically 6-10MB vs Electron's 150MB+

Quick Start

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

# Create new project with React
wails init -n myapp -t react-ts
cd myapp

# Development with hot reload
wails dev

# Build production binary
wails build
Enter fullscreen mode Exit fullscreen mode

Go Backend: Expose Functions to Frontend

package main

import (
    "context"
    "fmt"
    "os/exec"
    "runtime"
)

type App struct {
    ctx context.Context
}

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

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

// This function is automatically available in TypeScript!
func (a *App) Greet(name string) string {
    return fmt.Sprintf("Hello %s! Running on %s", name, runtime.GOOS)
}

// Access system resources that browsers can not
func (a *App) GetSystemInfo() map[string]string {
    return map[string]string{
        "os":      runtime.GOOS,
        "arch":    runtime.GOARCH,
        "cpus":    fmt.Sprintf("%d", runtime.NumCPU()),
        "version": runtime.Version(),
    }
}

// Run shell commands
func (a *App) RunCommand(cmd string) (string, error) {
    out, err := exec.Command("sh", "-c", cmd).Output()
    return string(out), err
}
Enter fullscreen mode Exit fullscreen mode

TypeScript Frontend: Call Go Functions

// Auto-generated TypeScript bindings!
import { Greet, GetSystemInfo, RunCommand } from "../wailsjs/go/main/App";

function App() {
  const [result, setResult] = useState("");
  const [sysInfo, setSysInfo] = useState<Record<string, string>>({});

  async function greet() {
    const greeting = await Greet("Developer");
    setResult(greeting);
  }

  async function loadSystemInfo() {
    const info = await GetSystemInfo();
    setSysInfo(info);
  }

  return (
    <div>
      <button onClick={greet}>Greet</button>
      <p>{result}</p>
      <button onClick={loadSystemInfo}>System Info</button>
      <pre>{JSON.stringify(sysInfo, null, 2)}</pre>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Native Features

import "github.com/wailsapp/wails/v2/pkg/runtime"

// File dialogs
func (a *App) OpenFile() (string, error) {
    return runtime.OpenFileDialog(a.ctx, runtime.OpenDialogOptions{
        Title: "Select File",
        Filters: []runtime.FileFilter{
            {DisplayName: "JSON Files", Pattern: "*.json"},
        },
    })
}

// System notifications
func (a *App) Notify(title, message string) {
    runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
        Type:    runtime.InfoDialog,
        Title:   title,
        Message: message,
    })
}

// Window control
func (a *App) Minimize() { runtime.WindowMinimise(a.ctx) }
func (a *App) ToggleFullscreen() { runtime.WindowToggleMaximise(a.ctx) }
Enter fullscreen mode Exit fullscreen mode

Size Comparison

Framework Hello World Size RAM Usage
Electron 150-200MB 80-150MB
Tauri 3-8MB 20-40MB
Wails 6-10MB 25-50MB
Native Qt 15-30MB 30-60MB

Useful Links


Building data-powered desktop apps? Check out my developer tools on Apify for ready-made web scrapers, or email spinov001@gmail.com for custom solutions.

Top comments (0)