Getting Started
ImmyGo is a high-level Go UI framework built on Gio that makes building beautiful desktop applications easy. It provides Fluent Design-inspired widgets, two API levels (declarative and lower-level), and built-in AI capabilities with pluggable providers (Ollama, Anthropic Claude, MCP).
Installation
1. System Dependencies
ImmyGo uses Gio for rendering, which requires platform-specific libraries:
Linux (Debian/Ubuntu):
sudo apt install -y libwayland-dev libxkbcommon-x11-dev libgles2-mesa-dev libegl1-mesa-dev libx11-xcb-dev
Linux (Fedora):
sudo dnf install wayland-devel libxkbcommon-x11-devel mesa-libGLES-devel mesa-libEGL-devel libX11-xcb
macOS:
xcode-select --install
Windows: No additional system dependencies needed.
2. Create Your Project
Manual setup:
mkdir myapp && cd myapp
go mod init myapp
go get github.com/amken3d/immygo
Using the CLI (recommended):
# Scaffold with default template
immygo new myapp
# Or generate with AI from a description (auto-detects Ollama, Anthropic, or MCP)
immygo new myapp --ai "a todo list with add and delete"
# With a specific provider
immygo new myapp --ai "a calculator" --provider ollama --model codellama
ANTHROPIC_API_KEY=sk-ant-... immygo new myapp --ai "a dashboard" --provider anthropic
See the AI Integration Guide for full provider setup instructions.
Choosing an API Level
ImmyGo offers two ways to build UIs:
Declarative ui Package |
Lower-Level widget/layout Packages |
|
|---|---|---|
| Best for | Most apps, rapid development | Custom widgets, fine-grained control |
| Gio knowledge | None required | Helpful but not mandatory |
| Imports | ui only |
widget, layout, theme, gioui.org/layout |
| State model | ui.State[T], ui.NewState() |
Package-level variables |
| Layout | ui.VStack(), ui.HStack() |
immylayout.NewVStack(), layout.Flex{} |
| Escape hatch | ui.ViewFunc() for raw Gio |
Full Gio access everywhere |
Recommendation: Start with the declarative ui package. Drop down to ViewFunc when you need raw Gio access for specific components.
Your First App — Declarative API
Create main.go:
package main
import (
"fmt"
"github.com/amken3d/immygo/ui"
)
func main() {
count := ui.NewState(0)
ui.Run("My First App", func() ui.View {
return ui.Centered(
ui.VStack(
ui.Text("Hello, ImmyGo!").Headline(),
ui.Text("Build beautiful UIs with Go."),
ui.Button("+1").OnClick(func() {
count.Update(func(n int) int { return n + 1 })
}),
ui.Text(fmt.Sprintf("Count: %d", count.Get())).Bold(),
).Spacing(12),
)
}, ui.Size(800, 600))
}
Run it:
go run main.go
You should see a centered window with a title, subtitle, button, and counter — all styled with the Fluent Light theme.
What’s Happening?
ui.Run()creates a window and starts the event loop- The build function is called every frame to produce the view tree
ui.NewState(0)creates reactive state that triggers re-rendersui.VStack()lays out children vertically with spacingui.Centered()centers the stack in the window.Headline(),.Bold()set text typography.OnClick()handles button clicks.Spacing(12)sets 12dp between children
No layout.Context, no layout.Dimensions, no Gio imports at all.
Your First App — Lower-Level API
package main
import (
"gioui.org/layout"
"github.com/amken3d/immygo/app"
immylayout "github.com/amken3d/immygo/layout"
"github.com/amken3d/immygo/theme"
"github.com/amken3d/immygo/widget"
)
func main() {
app.New("My First App").
WithSize(800, 600).
WithLayout(func(gtx layout.Context, th *theme.Theme) layout.Dimensions {
return immylayout.Center{}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
return immylayout.NewVStack().WithSpacing(16).Children(
func(gtx layout.Context) layout.Dimensions {
return widget.H1("Hello, ImmyGo!").Layout(gtx, th)
},
func(gtx layout.Context) layout.Dimensions {
return widget.Body("Build beautiful UIs with Go.").Layout(gtx, th)
},
).Layout(gtx)
})
}).
Run()
}
What’s Different?
- You use
app.New()instead ofui.Run() - Layout functions receive
gtx layout.Contextandth *theme.Theme - Every child is wrapped in
func(gtx layout.Context) layout.Dimensions { ... } - You call
.Layout(gtx, th)on each widget - You have full access to Gio’s constraint model
Core Concepts
1. Declarative Views (ui package)
Every element in the ui package implements the View interface. Views are composed by nesting:
ui.VStack(
ui.Text("Title").Title(),
ui.HStack(
ui.Button("Cancel").Outline(),
ui.Button("Save").OnClick(save),
).Spacing(8),
).Spacing(12).Padding(16)
Modifiers like .Padding(), .Background(), .Width() return *Styled, which supports all modifiers — so chains never hit a dead end.
2. Reactive State
The ui.State[T] type provides thread-safe reactive state:
name := ui.NewState("") // create
name.Set("Alice") // write
fmt.Println(name.Get()) // read
name.Update(func(s string) string { return s + "!" }) // atomic update
State changes are visible on the next frame because ui.Run() continuously re-calls your build function.
3. Widget State Persistence
In the declarative API, some widgets (like Input, Toggle, Checkbox) must be created outside the build function so their internal state (text content, toggle position, animation progress) persists across frames:
// CORRECT: persist across frames
name := ui.Input().Placeholder("Name")
agreed := ui.Checkbox("I agree", false)
ui.Run("App", func() ui.View {
return ui.VStack(name, agreed) // reuse same instances
})
Buttons are automatically cached by label, so ui.Button("Save") inside the build function is safe.
4. The Theme
Both API levels share the same theme system. The theme provides all visual tokens — colors, typography, spacing, corner radii, and elevation:
th.Palette.Primary // Accent color
th.Palette.Background // Window background
th.Typo.HeadlineLarge // TextStyle with Size, Weight, LineHeight
th.Space.MD // Medium spacing (12dp)
th.Corner.MD // Medium corner radius (8dp)
Two built-in themes:
theme.FluentLight()— Light Fluent Design theme (default)theme.FluentDark()— Dark Fluent Design theme
5. Event Handling
Declarative:
ui.Button("Save").OnClick(func() { fmt.Println("saved!") })
ui.Toggle(false).OnChange(func(on bool) { fmt.Println(on) })
ui.Input().OnChange(func(text string) { fmt.Println(text) })
Lower-level:
var btn = widget.NewButton("Save").WithOnClick(func() { fmt.Println("saved!") })
var toggle = widget.NewToggle(false).WithOnChange(func(on bool) { fmt.Println(on) })
6. Mixing API Levels
You can use ui.ViewFunc to drop into raw Gio anywhere in a declarative tree:
ui.VStack(
ui.Text("Declarative text"),
ui.ViewFunc(func(gtx layout.Context, th *theme.Theme) layout.Dimensions {
// Raw Gio code here — custom drawing, layout, etc.
return myCustomWidget.Layout(gtx, th)
}),
ui.Button("Back to declarative"),
)
And you can use ui.Themed() to access the current theme inside any view:
ui.Themed(func(th *theme.Theme) ui.View {
return ui.Text("Accent colored").Color(th.Palette.Primary)
})
Developer Tooling
ImmyGo includes CLI tools to speed up development:
# Live-reload dev server — watches files, rebuilds, restarts
immygo dev ./myapp/
# Dev server with AI assistant — type at ai> prompt to generate/modify UI
immygo dev --ai ./myapp/
# Dev server with a specific AI provider
immygo dev --ai --provider ollama --model qwen2.5-coder ./myapp/
# MCP server for Claude Code, Cursor, and other AI tools
immygo mcp
Runtime Prototyping
Generate UI from a description at runtime:
ui.Run("Prototype", func() ui.View {
return ui.Prototype("a settings page with dark mode toggle")
})
Layout Debugging
Inspect widget layout constraints and sizes:
IMMYGO_DEBUG=1 go run ./myapp/
See the AI Integration Guide for full details on all AI developer features.
Next Steps
- Widgets Reference — All controls with declarative and lower-level examples
- Layouts Guide — Composition patterns for both API levels
- Theming Guide — Customizing colors, typography, runtime theme switching
- AI Integration — AI capabilities, MCP server, dev tools, prototyping, and debugging