Shirei bets on pure-Go immediate-mode desktops
A flexbox, React-style UI layer for utility apps that refuse the browser stack.
Go has never lacked GUI options so much as a comfortable middle path. You can wrap Fyne or Qt and accept a retained widget graph (and often CGo friction). You can drop to Gio and own more of the render loop yourself. Or you can ship a Wails/webview shell and pretend the browser is a desktop toolkit. None of those feel like writing a small, self-contained utility the way you write a CLI.
Shirei is a Show HN-era framework that aims at that gap: immediate-mode UI in ordinary Go, flexbox-style layout, strong text, and almost no setup. It is explicitly for utility-style programs, not consumer polish or games. That narrow target is the interesting part. At v0.5 it is still work in progress, but the design choices map cleanly onto how many Go developers already think about state.
Immediate mode without the ImGui tax
Shirei's pitch is not "redraw everything every frame and hope." It is the React insight applied to native desktops: each frame you describe what the UI should look like from your data. You do not own widget objects, keep them in sync, or answer "did this button exist last frame?" Ordinary Go structs and slices are the model. View functions rebuild a container tree from that model.
In practice that looks like a short root function and attributes composed with small setters:
package main
import (
"fmt"
app "go.hasen.dev/shirei/app"
. "go.hasen.dev/shirei"
. "go.hasen.dev/shirei/widgets"
)
func main() {
app.SetupWindow("My App", 300, 100)
app.Run(RootView)
}
var count int
func RootView() {
Container(Attrs(Viewport, Background(220, 10, 97, 1)), func() {
Container(Attrs(Row, CrossMid, Pad(20), Gap(10)), func() {
Label(fmt.Sprintf("Counter: %d", count))
if Button(SymIPlus, "Increment") {
count++
}
})
})
}
Container opens a parent, runs a builder with that parent current, and children (labels, buttons, nested containers) hang off it. Widgets are functions that build containers; there is no separate object hierarchy to manage. Interaction returns boolean results for the current frame (if Button(...) { ... }), which is the usual immediate-mode contract and a natural fit for Go's lack of a reactive runtime.
Layout is flexbox-shaped: row or column, padding, gap, alignment, wrap, float, scroll, expand. Styling is attribute-driven rather than a full theme engine. That is closer to building a DOM tree and styling it than to wiring GTK containers or Material widgets, which is deliberate.
Where it sits in the Go GUI field
The comparison that matters is mental model plus distribution, not feature checklists.
Vs Fyne. Fyne is the default recommendation for many teams: Material-ish widgets, mobile targets, large community. It is retained-mode in spirit. You create and hold widgets, call Refresh(), and live inside its app/window lifecycle. Shirei rejects that bookkeeping. It also deprioritizes consumer chrome (no native file dialogs by design, no multi-window yet, no fancy graphics). If you need a polished cross-platform product UI and mobile, Fyne still wins. If you want a find-in-files tool or a pprof viewer that feels like a single Go binary, Shirei's model is lighter.
Vs Gio. Gio is the pure-Go immediate-mode reference, with a broader platform matrix (including mobile and WebAssembly) and a lower-level API. Shirei is higher level out of the box: full framework, stock widgets, flexbox containers, less boilerplate before you have a window. Gio is the better substrate if you are building a toolkit on top of an IM core. Shirei is closer to "open the repo and ship a utility."
Vs web shells (Wails, webview, HTML+CSS+JS). Those give you CSS layout and a huge ecosystem at the cost of a browser engine, JS bridges, and binary bulk that is hard to call "a small Go tool." Shirei's README calls out typical binary size around 10MB and self-contained Linux builds without system GUI deps. For developer tooling that is the right trade: you give up the web's widget zoo and keep a single-language stack.
Platform backends live in-tree (Cocoa, Win32, X11, Wayland). The surface is overwhelmingly Go; small Objective-C and C bits exist for platform glue. The product claim is native executables with identical-looking UIs across macOS, Windows, and Linux, not "zero native code anywhere."
What it means for your next tool
Adoption path is intentionally short. Module path is go.hasen.dev/shirei. Setup is app.SetupWindow plus app.Run(root). Docs and demos stress that AI agents can generate Shirei UI reasonably well because the API is small and declarative. That is a practical claim for throwaway internal tools, not marketing fluff.
The examples are not toys. The project points at real utilities: a live find-in-files list (haystack), a cross-platform process monitor, a native go tool pprof alternative, a Go binary module browser, a disk-usage visualizer, a system font viewer, an icon gallery, and a playable synth keyboard. Start with haystack if you only clone one. That catalog tells you the intended user: someone who already lives in terminals and occasional desktop utilities, not someone shipping a consumer SaaS client.
Text is a first-class feature, which is rare for "simple" IM libraries. Complex shaping, bidirectional layout, system fonts, and per-rune fallback are in scope. IME works on macOS and Windows; Linux IME is still incomplete. For CJK or RTL tooling that alone can beat a half-configured webview.
Headless rendering is built in: RenderToPNG / software rasterizer for snapshot tests and agent feedback loops. That is a real workflow advantage for CI and for generated UI, not a demo gimmick.
Trade-offs to budget for:
- Widget surface is developer-tooling first. No rich text, tree, or date picker yet. Custom controls mean composing containers yourself (the stock
Buttonis the reference). - No robust theming. Accent colors on some widgets; custom button looks are DIY. Styling can get verbose.
- Large text blocks hurt responsiveness unless you use the dedicated
LargeTextpath. - File paths use an in-app browser, not native OS dialogs (by design for now). Multiple windows and fancy graphics are out of scope.
- Mobile is undecided, and if it lands it will stay utility-class, not multi-touch product UI.
If you need OS-native chrome, accessibility polish beyond what an IM stack typically offers, or a full widget catalog, this is not production-ready for that job. If you are writing something in the du / process_monitor / internal dashboard class, it is worth a weekend prototype now.
Early, but the niche is real
Shirei does not invent immediate mode or flexbox. It packages them for Go with a deliberate product cut: no browser, no retained widget graph, strong text, small binaries, zero ceremony. The repo is young (modest star count, still marking known gaps in the open). That is fine. The risk is not that the idea is wrong; it is that the widget and theming surface stays too thin for anything beyond the author's own tools.
For professional Go developers the decision is narrow and concrete. Prefer Shirei when the app is a utility, state already lives in Go structs, identical cross-platform look matters more than native chrome, and you refuse to ship Chromium for a settings panel. Prefer Fyne or a web shell when you need a broader widget set, mobile, or design-system polish. Prefer Gio when you want the IM core and will build the rest yourself.
The framework that wins for a given shop is the one that matches the threat model of the UI: how much state sync, how much binary weight, how much platform chrome. Shirei's bet is that a large class of Go desktop work is closer to "React over a flexbox canvas" than to either GTK bindings or an embedded browser. On current evidence, that bet is coherent. Whether the catalog and Linux IME catch up will decide if it stays a clever personal toolkit or becomes the default answer for small native Go UIs.
Sources & further reading
- Show HN: Shirei, cross-platform GUI framework in native Go — github.com
- shi•rei - Cross platform GUI framework | Judi Systems — judi.systems
- shirei package - go.hasen.dev/shirei - Go Packages — pkg.go.dev
- Learning Go by examples: part 7 - Create a cross-platform GUI/Desktop app in Go - DEV Community — dev.to
- GUI - Awesome Go — awesome-go.com
Emeka has spent over a decade tracking threat actors, vulnerability disclosures, and the evolving landscape of application security, bringing a sharp continent-spanning perspective to his reporting. He's known for translating dense CVE advisories into clear, actionable context that developers and security teams alike actually read.
Discussion 0
No comments yet
Be the first to weigh in.