skills/gova-declarative-gui/SKILL.md
Build native desktop apps in Go using Gova's declarative, component-based GUI framework with reactive state and platform-native integrations.
npx skillsauth add aradotso/trending-skills gova-declarative-guiInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Skill by ara.so — Daily 2026 Skills collection.
Gova is a declarative GUI framework for Go that builds native desktop apps for macOS, Windows, and Linux from a single codebase. Views are plain Go structs, state is explicit via a Scope, and go build produces one static binary. Internally powered by Fyne (BSD-3), but the public API is stable and renderer-independent.
go get github.com/nv404/gova@latest
Optional CLI:
go install github.com/nv404/gova/cmd/gova@latest
Prerequisites:
build-essential + libgl1-mesa-dev (Linux), MinGW (Windows)| Command | Purpose |
|---|---|
| gova dev ./path/to/app | Hot reload — watch .go files, rebuild and relaunch on save |
| gova build ./path/to/app | Compile to ./bin/<name> static binary |
| gova run ./path/to/app | Build and launch once, no file watching |
| go build -ldflags "-s -w" | Stripped binary (~23 MB for simple apps) |
A component is a Go struct implementing Body(s *g.Scope) g.View. Fields on the struct are typed props; zero values are defaults.
package main
import g "github.com/nv404/gova"
type Greeting struct {
Name string // prop with zero-value default ""
}
func (c Greeting) Body(s *g.Scope) g.View {
name := c.Name
if name == "" {
name = "World"
}
return g.Text("Hello, " + name + "!").Font(g.Title)
}
State lives on the *g.Scope passed to Body. No hidden scheduler, no hook-ordering rules.
func (Counter) Body(s *g.Scope) g.View {
count := g.State(s, 0) // typed signal, initial value 0
return g.VStack(
g.Text(count.Format("Count: %d")).Font(g.Title),
g.HStack(
g.Button("-", func() { count.Set(count.Get() - 1) }),
g.Button("+", func() { count.Set(count.Get() + 1) }),
).Spacing(g.SpaceMD),
).Padding(g.SpaceLG)
}
func main() {
g.Run("My App", g.Component(MyComponent{}))
}
// Vertical stack
g.VStack(child1, child2, child3).Spacing(g.SpaceMD).Padding(g.SpaceLG)
// Horizontal stack
g.HStack(child1, child2).Spacing(g.SpaceSM)
// Layered/overlapping stack
g.ZStack(background, foreground)
// Scaffold (app shell with nav, toolbar, etc.)
g.Scaffold(
g.NavBar("Title"),
content,
)
| Constant | Use |
|---|---|
| g.SpaceSM | Small gaps |
| g.SpaceMD | Medium gaps |
| g.SpaceLG | Large padding |
g.Text("Hello").Font(g.Title) // styled text
g.Text("body text").Font(g.Body)
g.Button("Click me", func() { /* handler */ })
g.TextField(value.Get(), func(s string) { value.Set(s) })
g.Toggle(enabled.Get(), func(b bool) { enabled.Set(b) })
g.Image("path/to/image.png")
g.Spacer() // flexible space
g.Divider()
g.Title, g.Headline, g.Body, g.Caption, g.Mono
count := g.State(s, 0)
count.Get() // read
count.Set(42) // write, triggers re-render
count.Format("Value: %d") // returns formatted string signal
doubled := g.Derived(s, func() int {
return count.Get() * 2
})
g.Effect(s, func() {
fmt.Println("count changed to", count.Get())
}, count) // dependencies
name := g.PersistedState(s, "user-name", "")
package main
import g "github.com/nv404/gova"
type Todo struct {
Text string
Done bool
}
type TodoApp struct{}
func (TodoApp) Body(s *g.Scope) g.View {
todos := g.State(s, []Todo{})
input := g.State(s, "")
addTodo := func() {
if input.Get() == "" {
return
}
todos.Set(append(todos.Get(), Todo{Text: input.Get()}))
input.Set("")
}
rows := make([]g.View, 0, len(todos.Get()))
for i, todo := range todos.Get() {
i, todo := i, todo // capture loop vars
rows = append(rows, g.HStack(
g.Toggle(todo.Done, func(v bool) {
list := todos.Get()
list[i].Done = v
todos.Set(list)
}),
g.Text(todo.Text),
).Spacing(g.SpaceSM))
}
return g.VStack(
g.Text("Todos").Font(g.Title),
g.VStack(rows...).Spacing(g.SpaceSM),
g.HStack(
g.TextField(input.Get(), func(v string) { input.Set(v) }),
g.Button("Add", addTodo),
).Spacing(g.SpaceSM),
).Padding(g.SpaceLG)
}
func main() {
g.Run("Todo", g.Component(TodoApp{}))
}
// Alert dialog
g.Button("Alert", func() {
g.Alert(g.AlertOptions{
Title: "Warning",
Message: "Something happened.",
Style: g.AlertWarning,
})
})
// Open file dialog
g.Button("Open File", func() {
path, err := g.OpenFileDialog(g.OpenFileOptions{
Title: "Choose a file",
Extensions: []string{".txt", ".md"},
})
if err == nil && path != "" {
filePath.Set(path)
}
})
// Save file dialog
g.Button("Save", func() {
dest, err := g.SaveFileDialog(g.SaveFileOptions{
Title: "Save As",
DefaultFilename: "output.txt",
})
if err == nil && dest != "" {
// write to dest
}
})
// Dock badge (macOS)
g.DockBadge("3")
g.DockBadge("") // clear badge
// Dock progress (macOS)
g.DockProgress(0.75) // 0.0–1.0
g.DockProgress(-1) // hide
// Dock menu (macOS)
g.SetDockMenu([]g.MenuItem{
{Label: "New Window", Action: func() { /* ... */ }},
{Label: "Preferences", Action: func() { /* ... */ }},
})
// Dark/light toggle
g.Button("Toggle Theme", func() {
if g.CurrentTheme() == g.ThemeDark {
g.SetTheme(g.ThemeLight)
} else {
g.SetTheme(g.ThemeDark)
}
})
// Semantic colors in custom views
g.Text("Primary").Color(g.ColorPrimary)
g.Text("Secondary").Color(g.ColorSecondary)
g.Text("Danger").Color(g.ColorDanger)
type Card struct {
Title string
Content g.View
}
func (c Card) Body(s *g.Scope) g.View {
return g.VStack(
g.Text(c.Title).Font(g.Headline),
g.Divider(),
c.Content,
).Padding(g.SpaceMD)
}
// Usage
g.Component(Card{
Title: "My Card",
Content: g.Text("Card body text"),
})
type NotesApp struct{}
func (NotesApp) Body(s *g.Scope) g.View {
selected := g.State(s, "list")
switch selected.Get() {
case "detail":
return g.Component(DetailView{OnBack: func() { selected.Set("list") }})
default:
return g.Component(ListView{OnSelect: func() { selected.Set("detail") }})
}
}
func main() {
app := g.NewApp("My App")
app.SetIcon("assets/icon.png") // set before Run
app.Run(g.Component(MyComponent{}))
}
When using gova dev, use g.PersistedState to keep UI state across rebuilds:
func (MyApp) Body(s *g.Scope) g.View {
// survives hot reload, lost on full restart
activeTab := g.PersistedState(s, "active-tab", "home")
// ...
}
myapp/
├── main.go # g.Run entry point
├── components/
│ ├── header.go
│ └── sidebar.go
├── views/
│ ├── home.go
│ └── settings.go
├── assets/
│ └── icon.png
└── go.mod
| Feature | macOS | Windows | Linux | |---|---|---|---| | Core UI | ✅ | ✅ | ✅ | | Hot reload | ✅ | ✅ | ✅ | | App icon | ✅ | ✅ | ✅ | | Native dialogs | NSAlert/NSOpenPanel | Fyne fallback | Fyne fallback | | Dock/taskbar | NSDockTile ✅ | Planned | Planned |
cgo: C compiler not found
xcode-select --installsudo apt install build-essential libgl1-mesa-devgo build fails with OpenGL errors on Linux
sudo apt install libgl1-mesa-dev xorg-dev
Hot reload not picking up changes
gova dev, not go run.go files are in a directory watched by gova dev ./pathBinary is large (~32 MB)
go build -ldflags "-s -w" -o ./bin/myappState not updating the UI
count.Set(...) — mutating a slice/map in place without Set won't trigger re-renderlist := todos.Get(); list[i] = newVal; todos.Set(list)API breakage (pre-1.0)
go get github.com/nv404/[email protected]development
```markdown --- name: compose-performance-skills description: Install and use the skydoves/compose-performance-skills agent skill library to diagnose and fix Jetpack Compose performance issues including stability, recomposition, lazy layouts, modifiers, side effects, and build configuration. triggers: - "my composable recomposes too often" - "LazyColumn drops frames during scroll" - "diagnose Compose stability issues" - "fix unnecessary recomposition in Jetpack Compose" - "optimize Com
development
Headless iOS Simulator manager with host-side HID input injection, 60fps streaming, and device farm web UI for iOS 26
development
```markdown --- name: claude-code-game-studios description: Turn Claude Code into a full 49-agent game dev studio with 72 workflow skills, automated hooks, and a real studio hierarchy for Godot, Unity, and Unreal projects. triggers: - "set up claude code game studios" - "use ai agents for game development" - "set up game dev studio with claude" - "add game studio agents to my project" - "how do I use claude code for game dev" - "set up godot unity unreal ai workflow" - "49 agents g
development
```markdown --- name: xq-py-quantum-vm description: Python implementation of the Quip Network's quantum virtual machine (xqvm) triggers: - quantum virtual machine python - xqvm quip network - quantum circuit simulation python - xq-py quantum vm - quip network quantum python - simulate quantum gates python - quantum vm xqvm - xqvm-py quantum circuit --- # xq-py Quantum Virtual Machine > Skill by [ara.so](https://ara.so) — Daily 2026 Skills collection. `xqvm-py` is a Python impl