ToolGUI is a Go framework for quickly building interactive dashboards and internal tools — a Streamlit-like experience for Go. Write a plain Go function; ToolGUI renders it as a web page and reruns it on each interaction.
The same app runs three ways:
- Web server — a Go backend serving the page.
- Desktop — a native window via Wails (
toolgui-wails). - Browser only — compiled to WebAssembly, no server (
toolgui/tgwasm).
Warning
The API is still under development and may change.
- Live demo: https://voilelab.github.io/toolgui/demo/ — every component, running in your browser as WebAssembly.
- Docs: https://voilelab.github.io/toolgui/, built from
docs/. They track the latest release;docs/ondevmay be ahead.
go get github.com/voilelab/toolguipackage main
import (
"log"
"github.com/voilelab/toolgui/toolgui/tgcomp"
"github.com/voilelab/toolgui/toolgui/tgexec"
"github.com/voilelab/toolgui/toolgui/tgframe"
)
func Main(p *tgframe.Params) error {
name := tgcomp.Textbox(p.Sidebar, "What's your name?")
if name != "" {
tgcomp.Text(p.Sidebar, "Hi "+name+"~")
}
tgcomp.Text(p.Main, "hello ")
if tgcomp.Button(p.Main, "keep going") {
tgcomp.Text(p.Main, "world")
}
return nil
}
func main() {
app := tgframe.NewApp()
app.AddPage("index", "Index", Main)
e := tgexec.NewWebExecutor(app)
log.Println("Starting service...")
e.StartService("127.0.0.1:3000")
}Open http://127.0.0.1:3000.
Important
ToolGUI has no built-in auth: anyone who reaches the page can run the tool.
That is why the example binds to 127.0.0.1. To expose it further, put it
behind an authenticating proxy — see
Serving It Safely.
Branch from dev and open pull requests against dev; see
CONTRIBUTING.md.
- Go — version in
go.mod - Task — task runner
- Yarn — frontend
- Cypress — E2E tests
- mdBook — docs, see docs/README.md
task run_demo # component demo (web server)
task run_todo # small todo app built on state (cmd/toolgui-todo)
task run_wasm_hello # hello world in the browser (WebAssembly)
task run_wasm_demo # component demo in the browser
task run_desktop_hello # hello world as a desktop apptoolgui-wails is a separate Go module, since Wails v2 needs GTK and WebKit.
For your own desktop app, use go tool toolgui-desktop build or run; see
the desktop build instructions. Tagged
releases include the frontend, so consumers need no Node or Yarn.
cmd/toolgui-wasm builds the static site for the WebAssembly runtime; the
live demo is published from it.
The asset_app, asset_wails and asset_wasm tasks each prepare their own
prerequisites. When building several frontends, pass them to one invocation:
task asset_app asset_wails asset_wasmThe workspace dependencies are installed once, and the shared library is built and type-checked once, including its TypeScript declarations. Each frontend then builds and runs its own typecheck.
toolgui-web/web.go and toolgui-wails/assets.go embed build output that is
not committed on dev. Stub it to compile Go without a frontend build:
task stub_assets
go build ./...task stub_assets
go test ./...
task test_wasm # tgframe/tgwasm under js/wasm; needs Chrome on PATHE2E against the server demo (start task run_demo first):
cd toolgui-e2e
yarn
yarn e2e:chrome
yarn e2e:firefoxE2E against the WebAssembly demo (start task run_wasm_demo first):
task test_e2e_wasmRun the Release GitHub Action (Actions → Release → Run workflow) with
a vX.Y.Z version. It builds the web assets from dev, commits them, tags,
and pushes.
go getresolves tags, so the tagged commit must carrytoolgui-web/app/build— the only reason built assets live in git.mainis a CI-owned mirror of the latest release; never commit to it by hand.- Each release pushes two tags:
vX.Y.Zandtoolgui-wails/vX.Y.Z. Go resolves a submodule through a tag prefixed with its directory; without it,go geton the desktop module falls back todev's head, where the assets are gitignored.