Develop desktop apps with native UI or web frontend in Go
★ 5,801⑂ 351👁 15⑃ 1,106● 394▤ 80◈ 0
5,801
stars
+226
351
forks
15
watchers
1,106
prs
394
issues
80
releases
0
discussions
README
Desktop apps in Go, with native UI or a web frontend.
Sponsored by:
---
Each MyGo window shows one of two kinds of interface, and one app, or one
window, can mix them:
- A web page, in the webview the OS already has: WKWebView on macOS,
WebKitGTK on Linux, WebView2 on Windows. Build the frontend with any web
tools; it calls your Go code through a TypeScript client generated from it.
- Native UI, written in Go alone with package ui and
drawn by MyGo itself on the GPU: no HTML, no JavaScript and no webview, so
the window opens at once and takes little memory.
Either way, an app is a single Go binary of a few megabytes, focused on low
memory and CPU use.
- Pure Go, no cgo: build for every platform from any machine.
- Typed IPC for pages: bind Go services, stream values through channels
and send typed events; the TypeScript client is generated from your Go
code.
- A Go UI toolkit for native UI: flexbox and grid layout, widgets, text
editing with input methods, virtualized lists, SVG icons, animations,
screen reader support, and views you test without a window; Liquid Glass
on every platform with the glass plugin; web pages inside native UI, under
the menus and dialogs it shows over them.
- Desktop APIs for both: windows, menus, tray, dialogs, notifications,
global shortcuts, deep links, file associations, recent documents, Now
Playing and the media keys, the system accent color and accessibility
settings, watching files, and more.
- Ready to ship: app bundles and disk images, Windows installers, Debian
packages, PKGBUILDs for Arch Linux and a Linux install script, code signing, notarization, and signed
auto-updates with delta updates and an update window in the manner of
Sparkle.
A window with a web page, whose frontend calls Go:
go
type Greeter struct{}
// Greet returns a greeting.
func (Greeter) Greet(name string) string { return "Hello, " + name }
func main() {
mygo.Bind(Greeter{})
mygo.App.WhenReady(func() {
mygo.NewWindow(mygo.WindowOptions{Title: "Hello", URL: "/"})
})
if err := mygo.App.Run(); err != nil {
log.Fatal(err)
}
}
ts
import { Greeter } from "./mygo"; // generated
document.body.textContent = await Greeter.greet("Ada");
A window with native UI, whose view is a Go function of the app's state:
go
type counter struct{ n int }
func (s counter) view(c ui.Context) {
ui.Column(c).Fill().Center().Gap(12).Children(func() {
ui.Text(c, fmt.Sprint(s.n)).FontSize(40).Bold()
if ui.PrimaryButton(c, "Increment").Clicked() {
s.n++
}
})
}
func main() {
s := &counter{}
mygo.App.WhenReady(func() {
mygo.NewWindow(mygo.WindowOptions{
Title: "Counter",
Content: ui.View(s.view),
})
})
if err := mygo.App.Run(); err != nil {
log.Fatal(err)
}
}
Getting started
With Go 1.27.3+ and Bun, create an app
with a web frontend:
sh
bunx mygo-cli init my-app or: npx mygo-cli init my-app
cd my-app
bun run dev
An app of native UI needs Go alTop issues
#1444ui: Span.Blur, a blur on one run of a paragraph (CSS filter: blur() on an inline box)2 comments · 2026-11-07#1443ui: RadialGradient is a circle only (CSS radial-gradient's ellipse and extent keywords)2 comments · 2026-11-07#1304ui: text only sets across, so a column of Chinese is built a character at a time2 comments · 2026-11-05#419Let tests and agents drive a running app from another process2 comments · 2026-10-16#105[FEATURE] Native UI roadmap: prioritized capability improvements2 comments · 2026-10-06#1511A square Clip touching a rounded Clip's corner rasterizes a mask of the whole rounded clip1 comments · 2026-11-08#1496Opt-in way to present without display pacing (vsync off / uncapped) for benchmarking and latency measurement1 comments · 2026-11-08#1471Long rich text in virtualized lists: selection is lost when rows are recycled, and a long Markdown reply is built whole1 comments · 2026-11-07
Latest release
MyGo 0.8.3 v0.8.3 2026-11-07Changes in behavior No breaking changes to the Go API. Two changes code may notice: - Markdown's default parser reads GitHub mentions and references. parser.Default now includes alerts, mentions and issue references, so code that walks a document's inlines gets Mention and Reference inlines where it used to get Text. Their Text field holds the text as written, and parser.PlainText and the HTML writer handle them. To keep the old output, parse with parser.Default &^ (parser.ExtMentions | parser.ExtReferences). (1423) - Generated TypeScript types named Record or Promise get their package's pre