DEV Community

Cover image for How to Build Beautiful GUIs in Golang : 3 Web UI Paths
Sk
Sk

Posted on

How to Build Beautiful GUIs in Golang : 3 Web UI Paths

Web UIs often look like magic compared to native apps.

I used to think the web had some mythical language behind it. Turns out?
It's a C++ engine example Chrome, running CSS, JavaScript, and HTML. The elegance doesn’t come from the syntax, but from the ecosystem. Web dev moves fast because the market demands it.

Here's the fun part: almost every low-level language can hook into C or C++. Golang is no different.

You don’t need a miracle to build beautiful GUIs in Go. Just wire up a lightweight webview and let Go in the backend do what it does best: be fast and powerful.

Here are 3 battle-tested ways to do it:

  • Golang Wails – A production-ready webview stack with frontend templates baked in.
  • Web UI – Spin up the user’s existing browser and inject your HTML/CSS/JS directly.
  • Raw Webview – Like Wails, but with full control and flexibility.

With these tools, you can build gorgeous, fast desktop utility apps, all powered by Golang.


1. Wails: Full Web UI in Go with WebView

go mod init github.com/wailsexample
Enter fullscreen mode Exit fullscreen mode

Go to wails.io and follow the platform-specific setup. TL;DR:

Windows:

Install WebView2.

macOS:

xcode-select --install
Enter fullscreen mode Exit fullscreen mode

Linux:

Make sure you have:

gcc
libgtk-3
libwebkit2gtk
Enter fullscreen mode Exit fullscreen mode

Then install the Wails CLI:

go install github.com/wailsapp/wails/v2/cmd/wails@latest
Enter fullscreen mode Exit fullscreen mode

Check your setup:

wails doctor
Enter fullscreen mode Exit fullscreen mode

Wails includes frontend templates:

  • Svelte
  • React
  • Vue
  • Preact
  • Lit
  • Vanilla

We’ll go with Vanilla JS, simple and fast:

wails init -n wailsapp -t vanilla
Enter fullscreen mode Exit fullscreen mode

Key project files:

/main.go         → your Go backend
/frontend/       → your frontend (JS, HTML, CSS)
/wails.json      → config
Enter fullscreen mode Exit fullscreen mode

Start the app:

wails dev
Enter fullscreen mode Exit fullscreen mode

Wails handles everything under the hood.

Embedding Frontend Assets

In main.go this line:

//go:embed all:frontend/dist
var assets embed.FS
Enter fullscreen mode Exit fullscreen mode

This loads your built frontend files. You can change the directory to point to any project with an index.html.

Binding Go Functions to the Frontend

In main.go, define and bind a function to the App struct:

func (a *App) Hello(name string) string {
    return fmt.Sprintf("Hello %s!", name)
}
Enter fullscreen mode Exit fullscreen mode

Wails will expose it to the frontend.

In JS:

import { Hello } from "../wailsjs/go/main/App"

Hello("Sk").then(alert).catch(console.error)
Enter fullscreen mode Exit fullscreen mode

All bound functions are async.

More details in the Wails dev guide.

Build for Production

wails build
Enter fullscreen mode Exit fullscreen mode

That’s it. I used Wails in production at a company, it Just. Works.


2. WebUI: Use the User’s Default Browser

WebUI skips WebView entirely and launches the user’s installed browser. Lightweight and clever, but a bit limited. Some methods haven’t been ported to Go yet.

Use it if:

  • You want minimal setup.
  • You don’t need to reuse windows in the same process(weird memory leak).

Setup

go mod init github.com/webuiexample
Enter fullscreen mode Exit fullscreen mode

Then run:

Windows:

Invoke-WebRequest https://raw.githubusercontent.com/webui-dev/go-webui/main/setup.bat -OutFile setup.bat
./setup.bat
Enter fullscreen mode Exit fullscreen mode

macOS/Linux:

sh -c "$(curl -fsSL https://raw.githubusercontent.com/webui-dev/go-webui/main/setup.sh)"
./setup.sh
Enter fullscreen mode Exit fullscreen mode

Project Structure

/ui/index.html
main.go
Enter fullscreen mode Exit fullscreen mode

Sample main.go

package main

import (
    "time"
    ui "github.com/webui-dev/go-webui/v2"
)

const w = ui.Window(1)

func exit(e ui.Event) any {
    ui.Exit()
    return nil
}

func main() {
    ui.SetDefaultRootFolder("ui")
    w.NewWindow()
    w.ShowBrowser("index.html", ui.ChromiumBased)
    w.Bind("exit", exit) // exposes function to the frontend

    go getLocalStoreContents()

    ui.Wait()
}

func getLocalStoreContents() {
    time.Sleep(5 * time.Second)
    content, _ := w.Script(`return localStorage.getItem("content");`, ui.ScriptOptions{})
    println(content)
}
Enter fullscreen mode Exit fullscreen mode

In your frontend (index.html):

<script src="webui.js"></script>
<script>
     localStorage.setItem("content", "Hello World!");  // getLocalStoreContents grabs this
    setTimeout(() => {

        webui.exit();  // will call exit after 5 seconds
    }, 5000);
</script>
Enter fullscreen mode Exit fullscreen mode

getLocalStoreContents injects a runs a script in the web, in our case fetching contents in the local storage:

   content, _ := w.Script(`return localStorage.getItem("content");`, ui.ScriptOptions{})
Enter fullscreen mode Exit fullscreen mode

You can also call JS functions from Go:

e.Window.Run(`SetCount(10);`)
Enter fullscreen mode Exit fullscreen mode

And define it in JS:

function SetCount(value) {
    console.log("Count is", value)
}
Enter fullscreen mode Exit fullscreen mode

Pretty straightforward.


3. Webview: Embedded Simplicity, Full Control

This one’s my favorite.

It’s like WebUI, but embedded like Wails. Lightweight, flexible, and you control the browser.

Make sure WebView is installed (if you followed the Wails setup, you’re good).

Setup

go mod init github.com/webviewexample
go get github.com/webview/webview_go
Enter fullscreen mode Exit fullscreen mode

main.go

package main

import (
    "fmt"
    webview "github.com/webview/webview_go"
)

func main() {
    w := webview.New(true)  // create the webview
    defer w.Destroy()

    w.SetTitle("Webview")
    w.SetSize(600, 420, webview.HintNone)

    w.Bind("hello", func() string {   // same as webui exposing the function
        return "Hello from Webview!"
    })

    w.Navigate("C:/path/to/index.html")  // loading the frontend
    w.Run()
}
Enter fullscreen mode Exit fullscreen mode

In your HTML:

<script>
    setTimeout(() => {
        window.hello().then(alert)
    }, 3000);
</script>
Enter fullscreen mode Exit fullscreen mode

Just like WebUI, but with more control.

Asset Loading

Paths can be a nightmare in any language, because of the difference between systems.

And how go handle binary when using go run . the binary is place in a random generated(UUID like) folder. so assets are hard to find.

So You’ll want to either:

  • Use embed.FS
  • Or spin up a tiny server mux

I've got a mini continuation article on Coffee & Kernels where I walk you through both approaches, perfect for React or more complex apps.


So Which One?

  • Use Wails if you want to ship a polished app fast.
  • Use Webview if you want low-level control and stability.
  • Use WebUI if you want something quick and clever, but don’t need much flexibility.

Wrapping Up

We explored 3 ways to build GUI apps with Golang:

  • Wails : Full framework for production-ready apps
  • WebUI : Lightweight, browser-based UIs
  • Webview : Embedded, low-level control

All 3 do the same thing, serve a frontend with Go behind the scenes. Which one you pick depends on your use case.

Know any elegant or underrated ways to build low-level UIs I didn’t mention? Or built something with one of these approaches? Drop it in the comments; I’d love to see what you’ve made.

And hey, once you realize everything eventually boils down to C, C++, and ultimately assembly. you start to see that you can inject anything into everything.

I’ll be posting more deep dives on backend topics, Golang, and low-level systems on Substack. Would love to have you there; come say hi:

Coffee & Kernels

Top comments (0)