Fork me on GitHub

One widget tree,
every surface.

A widget that calls the platform's own button is as portable as the least capable platform it targets. go-widgets draws every widget itself, through one small painter interface, and lets a back-end decide whether that becomes the pixels of a window, a browser canvas, an Android surface โ€” or the cells of a terminal. Pure Go, CGO_ENABLED=0. BSD-3-Clause.

160 widget types8 window back-ends, one OpenCGO=0 on every target
go-widgets/window โ€” zsh
# one demo, cross-compiled from one machine, cgo off
$ for t in linux/amd64 linux/riscv64 \
    linux/s390x darwin/arm64 windows/arm64 \
    android/arm64 js/wasm; do
  CGO_ENABLED=0 GOOS=${t%/*} GOARCH=${t#*/} \
    go build -o /dev/null ./cmd/windowdemo \
    && echo "ok   $t"
  done
ok   linux/amd64
ok   linux/riscv64
ok   linux/s390x
ok   darwin/arm64
ok   windows/arm64
ok   android/arm64
ok   js/wasm

# the one Go itself refuses
$ CGO_ENABLED=0 GOOS=android GOARCH=arm \
    go build ./cmd/windowdemo
android/arm requires external (cgo) linking, but cgo is not enabled
19 modules โ€” one widget tree, drawn the same way everywhere
Why go-widgets

A widget never learns where it is drawn.

Its Draw talks to a painter: fill a rectangle, stroke one, set a pixel, draw text. Whether those calls become pixels or terminal cells is the back-end's business. That is the whole reason one widget tree runs in a native window, a browser and a terminal without a line of conditional code.

๐Ÿ–Œ๏ธ

Drawn by the toolkit, everywhere

No webview, no system widget set, no C toolchain. 160 widget types โ€” a data suite with Table, Kanban, Gantt and an event calendar, eight charts, menus, dialogs โ€” drawn the same way on every surface, tested once.

๐Ÿงญ

Chosen by what is there

window.Open takes no back-end argument. It reads the environment โ€” the platform, $WAYLAND_DISPLAY, $DISPLAY, an Android host's socket โ€” and opens the window that can work. The same Android binary dials its host inside an APK and opens a Linux window under Termux.

๐Ÿ”Œ

Without cgo, all the way down

X11 is the core protocol written from scratch over the socket. macOS is reached through the Objective-C runtime, Windows through the process's own DLLs, both from Go. Android, which hands no surface to a CGO-free process, gets a thin Java host that only blits.

๐Ÿ”

State lives in a view-model

Observables, commands and lists, bound to widgets once. The core imports no widget package, so one view-model drives the pixel widgets and the terminal ones. mvvmlint fails a pull request that assigns a widget's field by hand.

โ™ฟ

Accessibility it publishes itself

A toolkit that paints its own pixels gives a screen reader nothing โ€” unless it says what it drew. Widgets report a role, a name and a value; the back-ends publish them: AT-SPI on Linux, a virtual view hierarchy on Android, pulled only when something reads it.

๐Ÿ”ค

Text without a C library

One call switches the whole UI to anti-aliased, shaped text in a bundled face, rasterised by go-opentype in Go โ€” no FreeType, no system font lookup, the same in a browser. GTK and libadwaita themes load as a Theme.

๐ŸŽจ

Widgets as data, when you want them

A skin describes a widget's parts, states and transitions in JSON, rendered by one engine. The toolkit's own button, switch and card, written as skins, render pixel for pixel the same.

๐Ÿ—‚๏ธ

The same data, local or remote

A typed store queries an in-process proxy or a gRPC one over a WebSocket, and the conformance test requires the two to return byte-identical views โ€” so a wasm client and a native one see the same rows.

โš–๏ธ

Proven on real displays

Pixels sampled from a real X11 server under Xvfb and from a macOS window in CI, clicks driven through a real Win32 window procedure on Windows 11, composited pixels read back from the wasmdesk compositor in Chromium, blits timed on an Android device. What is only compiled is said too.

Where it paints

6 surfaces, one widget tree.

Linux

Wayland (xdg-shell) or X11 (the core protocol over the socket, MIT-SHM), chosen from the session; GTK4 on request.

macOS

An NSWindow through the Objective-C runtime with purego; a menu-bar NSStatusItem for the tray.

Windows

A Win32 window through the process’s own DLLs and a Go window procedure; Shell_NotifyIcon for the tray.

Browser

A plain <canvas>, through webcanvas or window.Open itself, or a window of the wasmdesk compositor over a MessagePort and a shared buffer.

Terminal

The cell painter, serialised as 24-bit ANSI; an interactive runner with raw mode and the alternate screen.

Android

A real APK: a Java host blits a memfd the Go process paints; input and accessibility cross a socket. arm64.

160
widget types in toolkit v0.329.0, counted at that tag
8
window back-ends behind one window.Open
19
modules, each CGO_ENABLED=0
19
of them gated at 100% statement coverage in CI
The modules

Pick the layer you need.

painter v0.16.0

The seam every widget draws through: seven primitives, optional capabilities (clip, paths, images, masks, faces), a pixel painter and a cell painter. Docs โ†’

toolkit v0.329.0

160 widget types: inputs, containers with swappable layouts, menus and dialogs, a data suite (Table, TreeTable, Kanban, Gantt, Agenda), eight charts. GTK themes, OpenType text, touch, accessibility. Docs โ†’

skin v0.3.0

A widget’s parts, states and transitions in a .skin.json file. The toolkit’s button, check, switch, chip and card, rewritten as skins, render pixel for pixel the same. Docs โ†’

isoicons v0.4.0

Isometric icon packs โ€” 44 cloud-native, 9 AWS โ€” for the toolkit’s isometric diagrams. Assets vendored unmodified, each under its own licence. Docs โ†’

mvvm v0.15.0

Observable, Command, ObservableList, binders, and an undo stack. Generics, no reflection; the core imports no widget package, so one view-model drives pixels and cells. Docs โ†’

mvvmtk v0.15.0

One call binds a toolkit widget to a view-model, with the widget’s real field and callback names filled in. Every binder returns an unbind. Docs โ†’

data v0.4.0

Typed records, a pure query engine, and a store whose in-process and gRPC proxies return byte-identical views โ€” natively and in wasm. Docs โ†’

window v0.89.1

One Open/Run over X11, Wayland, GTK4, Cocoa, Win32, Android, wasmbox and a browser tab. X11 is the core protocol written from scratch over the socket; macOS and Windows are reached without cgo. Docs โ†’

application v0.8.1

The lifecycle above a window: the run loop, a tray icon attached once the first frame is up, the live system appearance, the accessibility tree, a ready hook. Docs โ†’

tray v0.15.0

A menu-bar or notification-area icon with menus, submenus and checkboxes: NSStatusItem, Shell_NotifyIcon, StatusNotifierItem over DBus. No build tag; ErrNoBackend where there is none. Docs โ†’

webcanvas v0.4.1

A scene in a plain browser <canvas>: no compositor, no SharedArrayBuffer, no cross-origin isolation. Any static host serves it. Docs โ†’

tui v0.62.0

The same widgets through the cell painter, an interactive runner (raw mode, alternate screen, resize), and cell-native widgets, a syntax-highlighting editor among them. Docs โ†’

android v0.16.0

An installable APK whose whole interface is painted by a CGO_ENABLED=0 Go process; a thin Java host blits a shared memfd and owns nothing else. Docs โ†’

svg v0.7.0

Any RGBA render as a self-contained, bit-exact SVG, or as a PNG. The widget catalogue rendered this way: go-widgets.github.io/svg. Docs โ†’

mvvmlint v0.4.2

A go vet check that fails a pull request assigning a widget’s state field by hand, or shipping widgets with no view-model. Docs โ†’

bricolint v0.4.1

A go vet check against hand-drawn UI: no painter primitives in application code, no widget rebuilt on every frame. An exemption must give its reason. Docs โ†’

app-template v0.5.0

The starting point: a browser app with its state in a view-model, a 100% coverage gate and the MVVM gate, green from the first commit. Docs โ†’

gallery v0.7.0

Every widget family, live in a browser canvas, and driven: drag Kanban cards, resize Gantt bars, switch themes. Open it. Docs โ†’

desktop v0.20.0

A desktop shell โ€” dock, launcher, application menu, file grid, notifications โ€” composed from go-freedesktop and go-widgets, from one source natively and in wasmdesk. Docs โ†’

A UI toolkit that ships like Go.

No cgo, no webview, no runtime to install. One go build per target, from any machine, and the same widget tree in a window, a browser tab, a terminal or an APK. Start from app-template: MVVM-compliant and green from the first commit.

Read the documentation โ†’