A reactive UI toolkit for Go, built on Willow and Ebitengine. WillowUI provides a complete widget library, reactive state management, JSON theming, and XML templating for building desktop and game UIs.
New here? Check out the WillowUI website for guides, examples, and everything you need to start building with WillowUI.
Status: Actively developed. API may change before v1.0.0.
- 50+ widgets -- buttons, text inputs, sliders, lists, trees, data tables, menus, color pickers, modals, toasts, and more
- Reactive state --
Ref,Computed,WatchEffect, reactiveArrayandRecordtypes that drive automatic UI updates - JSON theming -- style every widget property from a single JSON file; ships with 7 built-in themes
- XML templates -- declare layouts in XML with reactive bindings, conditionals, and event handlers
- Layout system -- VBox, HBox, Anchor, Flow, and TwoColumn layouts with padding, spacing, and alignment
- Rich text -- inline markup for bold, italic, color, and size within a single text widget
- Focus management -- tab navigation, focus rings, and keyboard-driven interaction
- Screen management -- push, pop, and replace screens with a built-in stage manager
- Visual test runner -- automated screenshot testing via JSON scripts and input injection
go get github.com/devthicket/willowuipackage main
import (
"github.com/devthicket/willow"
ui "github.com/devthicket/willowui"
)
func main() {
font := ui.MustLoadDefaultFont()
clicks := ui.NewRef(0)
btn := ui.NewButton("inc", "+1", font, 16)
btn.SetOnClick(ui.Increment(clicks, 1))
label := ui.NewLabel("count", "0", font, 36)
formatted, _ := ui.BindFormatterf(clicks, "Count: %d") // app-lifetime: handle not stopped
label.BindText(formatted)
row := ui.NewHBox("row")
row.Spacing = 16
row.SetPosition(20, 15)
row.AddChild(btn)
row.AddChild(label)
ui.Setup(ui.StageConfig{
Title: "Counter",
Width: 400,
Height: 70,
ClearColor: willow.RGBA(0.08, 0.08, 0.10, 1),
}, row)
}| Category | Widgets |
|---|---|
| Input | Button, IconButton, TextInput, TextArea, MaskedInput, KeybindInput, SearchBox |
| Selection | Checkbox, RadioButton, Toggle, OptionRotator, Select |
| Range | Slider, NumberStepper, ProgressBar, MeterBar, ScrollBar |
| Lists | List, TreeList, TileList, SortableList, SortableTreeList, DataTable, TreeTable |
| Containers | Panel, Window, ScrollPanel, NavDrawer, Accordion, Popover |
| Navigation | TabBar, ToggleButtonBar, ToolBar, MenuBar |
| Display | Label, RichText, Badge, Tag, TagBar, Tooltip, Toast, Image, AnimatedImage |
| Specialty | ColorPicker, GradientEditor, ImageCropper, TimePicker, CalendarSelector, StatWeb, DragHandle, InputField |
Themes are plain JSON. Load one and every widget picks it up:
theme, err := ui.LoadThemeFromFile("themes/dark.json")
ui.SetTheme(theme)Ships with 7 built-in themes: dark, forest, jrpg, macos, neon, windows, and debug. Create your own -- each widget type has its own section with full control over colors, corners, padding, and more. See the theme docs for the full schema.
Define layouts declaratively and bind reactive state:
<VBox padding="16" spacing="8">
<Label text="Hello, {{name}}" fontSize="24" />
<Button text="Click me" on:click="handleClick" />
</VBox>Templates compile to a binary format for fast startup, and support hot reload during development.
The examples/ directory has 60+ runnable demos:
go run ./examples/widgets/buttons/
go run ./examples/reactive/counter/
go run ./examples/templating/xml-basic/
go run ./examples/theming/theme-gallery/- WillowUI -- Homepage, guides, and tutorials
- Documentation -- In-depth guides and theming cookbook
- API Reference -- Full API documentation
- Go 1.24+
- Willow -- scene graph rendering engine
- Ebitengine v2.9+ -- GPU backend
Contributions are welcome. Please open an issue first for major changes to discuss the design. For bug fixes and small improvements, open a pull request directly.
go build ./...
go test ./...
go vet ./...




























