Guix
Guix is Go for the UI - A complete Go-based UI language that transpiles to WebAssembly, featuring reactive components, virtual DOM, and type-safe event handling.
Features
- ๐ Pure Go - Write UI components in Go-like syntax
- โก Virtual DOM - Efficient diffing and patching with keyed reconciliation
- ๐ Reactive - Channel-based state management for real-time updates
- ๐ฆ Type-Safe - Full Go type system with compile-time checks
- ๐ฏ WebAssembly - Compiles to WASM for native browser performance
- ๐ ๏ธ Developer Tools - Watch mode, incremental compilation, and hot reload
- ๐จ Template Interpolation - Backtick strings with expression interpolation
- ๐ฎ WebGPU Support - First-class 3D graphics with scene graphs, PBR materials, and lighting
- ๐ 2D Charts - GPU-accelerated charting with candlestick, line, and bar charts
Quick Start
Installation
go install github.com/gaarutyunov/guix/cmd/guix@latest
Create Your First Component
Create a file hello.gx:
package main
func Hello(name: string) {
Div {
H1 {
`Hello, {name}!`
}
}
}
Generate Go Code
guix generate -p .
This creates hello_gen.go with the compiled component.
Build for WebAssembly
# Generate components
guix generate -p .
# Copy wasm_exec.js
cp $(go env GOROOT)/misc/wasm/wasm_exec.js .
# Build WASM
GOOS=js GOARCH=wasm go build -o main.wasm .
Language Syntax
Components
Components are the building blocks of Guix applications:
func Button(label: string, onClick: func(Event)) {
Button(OnClick(onClick)) {
Text(label)
}
}
Parameter Passing Styles
Guix supports multiple ways to pass parameters to components:
1. Normal Parameters (Default)
By default, components accept parameters directly:
func Button(label string, onClick func(Event)) (Component) {
Button(OnClick(onClick)) {
`{label}`
}
}
// Generated constructor:
// func NewButton(label string, onClick func(Event)) *Button
// Usage:
btn := NewButton("Click Me", handleClick)
2. Auto Props with @props Directive
Use the @props directive to automatically generate props structs and option functions:
@props func Button(label string, onClick func(Event)) (Component) {
Button(OnClick(onClick)) {
`{label}`
}
}
// Generated code:
type ButtonProps struct {
Label string
OnClick func(Event)
}
type ButtonOption func(*Button)
func WithLabel(v string) ButtonOption { ... }
func WithOnClick(v func(Event)) ButtonOption { ... }
// Generated constructor:
// func NewButton(opts ...ButtonOption) *Button
// Usage:
btn := NewButton(
WithLabel("Click Me"),
WithOnClick(handleClick),
)
3. Manual Props Struct
Define your own props struct:
type ButtonProps struct {
Label string
OnClick func(Event)
}
func Button(props ButtonProps) (Component) {
Button(OnClick(props.OnClick)) {
`{props.Label}`
}
}
// Generated constructor:
// func NewButton(props ButtonProps) *Button
// Usage:
btn := NewButton(ButtonProps{
Label: "Click Me",
OnClick: handleClick,
})
4. Variadic Parameters
Use Go's variadic syntax for variable-length arguments:
func MessageList(messages ...string) (Component) {
Div {
for _, msg in messages {
P { `{msg}` }
}
}
}
// Generated constructor:
// func NewMessageList(messages ...string) *MessageList
// Usage:
list := NewMessageList("Hello", "World", "From", "Guix")
See the params example for detailed comparisons and use cases.
Template Interpolation
Use backticks for template strings with embedded expressions:
func Counter(count: int) {
Div {
`Count: {count}`
}
}
Channel-Based State
Channels enable reactive, real-time updates:
import "strconv"
func Counter(counterChannel: chan int) {
Div(Class("counter-display")) {
Span(Class("counter-value")) {
`Counter: {<-counterChannel}`
}
}
}
func App() {
counter := make(chan int, 10)
Div(Class("app-container")) {
H1 {
"Counter Example"
}
Counter(WithCounterChannel(counter))
Div(Class("input-group")) {
Input(
Type("number"),
Placeholder("Enter a number"),
OnInput(func(e: Event) {
value := e.Target.Value
n, _ := strconv.Atoi(value)
counter <- n
})
)
}
}
}
Event Handlers
Type-safe event handling with Go functions:
func Form() {
Div {
Input(
OnInput(func(e: Event) {
value := e.Target.Value
// Handle input
}),
Placeholder("Enter text...")
)
Button(
OnClick(func(e: Event) {
e.Native.Call("preventDefault")
// Handle click
})
) {
Text("Submit")
}
}
}
Element Builders
Common HTML elements with type-safe APIs:
Div(Class("container")) {
H1 {
Text("Title")
}
P(Style("color: blue;")) {
Text("Paragraph text")
}
Button(
ID("submit-btn"),
OnClick(handleSubmit)
) {
Text("Submit")
}
Img(
Src("/image.png"),
Attr{Key: "alt", Value: "Description"}
)
}
CLI Commands
Generate
Generate Go code from .gx files:
# Generate all .gx files in current directory
guix generate
# Specify path
guix generate -p ./components
# Watch mode - auto-regenerate on changes
guix generate -w
# Lazy mode - only regenerate changed files
guix generate --lazy
# Verbose output
guix generate --verbose
Clean
Remove generated files and cache:
guix clean
guix clean -p ./components
Architecture
Runtime Library
The runtime (pkg/runtime) provides:
- VNode: Virtual DOM node structure
- Diffing: Efficient tree comparison with keyed reconciliation
- DOM Manipulation: syscall/js wrappers for DOM operations
- Scheduler: requestAnimationFrame batching for performance
- Event System: Memory-safe event handler management
- WebGPU: 3D graphics with scene graphs, shaders, buffers, and pipelines
- Math: 3D vectors, matrices, and transformations
- Geometries: Built-in primitives (box, sphere, plane)
Code Generator
The code generator (pkg/codegen) uses Go's AST package to:
- Parse
.gx files with participle
- Generate Props structs for type safety
- Create option functions for ergonomic APIs
- Build Render methods with virtual DOM
- Output formatted Go source code
Parser
The parser (pkg/parser) implements:
- Stateful lexer for template interpolation
- LL(k) recursive descent parsing with participle
- Support for Go-like expressions and statements
- Channel type syntax (
<-chan T, chan T)
Tech Stack
Guix is built with the following core dependencies:
Parser & Code Generation
- participle/v2 - LL(k) parser for the
.gx syntax
- go/ast - Go's AST package for code generation
- go/format - Automatic formatting of generated Go code
Runtime
- syscall/js - WebAssembly JavaScript interop
- sync - Goroutine synchronization primitives
- context - Cancellation and timeout handling
- Standard Go toolchain (go 1.24+)
- WebAssembly target support (
GOOS=js GOARCH=wasm)
- Optional: TinyGo for smaller binary sizes
Examples
Counter
See examples/counter/ for a complete counter application demonstrating:
- Component composition with the
Counter component
- Reactive channel-based state management
- Event handling with
OnInput
- Template interpolation with backtick strings
- Type-safe props and option functions
The example consists of two components in .gx files:
counter.gx - Display component that renders channel values:
func Counter(counterChannel: chan int) {
Div(Class("counter-display")) {
Span(Class("counter-value")) {
`Counter: {<-counterChannel}`
}
}
}
app.gx - Main app component with input handling:
import "strconv"
func App() {
counter := make(chan int, 10)
Div(Class("app-container")) {
H1 {
"Counter Example"
}
Counter(WithCounterChannel(counter))
Div(Class("input-group")) {
Input(
Type("number"),
Placeholder("Enter a number"),
ID("counter-input"),
OnInput(func(e: Event) {
value := e.Target.Value
n, _ := strconv.Atoi(value)
counter <- n
})
)
}
}
}
To run:
cd examples/counter
./build.sh
python3 -m http.server 8080
# Open http://localhost:8080
WebGPU Rotating Cube
See examples/webgpu-cube/ for a complete 3D rendering example demonstrating:
- WebGPU initialization and canvas setup
- 3D scene graph with meshes, cameras, and lights
- PBR (physically-based rendering) materials
- Real-time rotation and user controls
- Keyboard and mouse interaction
- Render loop with requestAnimationFrame
Quick example:
// Create scene with a rotating cube
scene := runtime.Scene(
runtime.Background(0.1, 0.1, 0.15, 1.0),
)
// Add cube mesh
cube := runtime.Mesh(
runtime.GeometryProp(runtime.NewBoxGeometry(2, 2, 2)),
runtime.MaterialProp(runtime.StandardMaterial(
runtime.Color(0.91, 0.27, 0.38, 1.0),
runtime.Metalness(0.3),
runtime.Roughness(0.4),
)),
)
// Add camera
camera := runtime.PerspectiveCamera(
runtime.FOV(runtime.DegreesToRadians(60)),
runtime.Position(0, 2, 6),
runtime.LookAtPos(0, 0, 0),
)
// Add lights
ambient := runtime.AmbientLight(runtime.Intensity(0.4))
directional := runtime.DirectionalLight(
runtime.Position(5, 10, 7),
runtime.Intensity(0.8),
)
scene.Children = append(scene.Children, cube, camera, ambient, directional)
// Create renderer and start render loop
renderer, _ := runtime.NewSceneRenderer(canvas, scene)
canvas.SetRenderFunc(func(c *runtime.GPUCanvas, delta float64) {
renderer.Render()
})
canvas.Start()
To run:
cd examples/webgpu-cube
cp $(go env GOROOT)/misc/wasm/wasm_exec.js .
GOOS=js GOARCH=wasm go build -o main.wasm
python3 -m http.server 8080
# Open http://localhost:8080
Requirements: Chrome 113+, Edge 113+, or Safari Technology Preview with WebGPU support.
WebGPU Chart Example
See examples/webgpu-chart/ for a complete 2D charting example demonstrating:
- GPU-accelerated rendering for high-performance charts
- Candlestick charts for OHLCV (Open-High-Low-Close-Volume) data
- Declarative chart definition with Guix components
- Time-based X-axis with automatic formatting
- Price formatting on Y-axis
- Grid lines and axis labels
- Responsive design
Quick example:
// Define chart data
chartData := []chart.OHLCV{
{Timestamp: 1701388800000, Open: 37500, High: 38200, Low: 37100, Close: 37800, Volume: 28500000000},
{Timestamp: 1701475200000, Open: 37800, High: 39100, Low: 37600, Close: 38900, Volume: 32100000000},
// ... more data
}
// Create chart declaratively
Chart(ChartBackground(0.08, 0.09, 0.12, 1.0)) {
XAxis(
AxisPosition("bottom"),
TimeScale(true),
GridLines(true),
)
YAxis(
AxisPosition("right"),
GridLines(true),
)
CandlestickSeries(
ChartData(chartData),
UpColor(0.18, 0.80, 0.44, 1.0), // Green for bullish candles
DownColor(0.91, 0.27, 0.38, 1.0), // Red for bearish candles
WickColor(0.6, 0.6, 0.65, 1.0),
BarWidth(0.8),
)
}
To run:
cd examples/webgpu-chart
go generate
GOOS=js GOARCH=wasm go build -o main.wasm
cp $(go env GOROOT)/misc/wasm/wasm_exec.js .
python3 -m http.server 8080
# Open http://localhost:8080
Requirements: Chrome 113+, Edge 113+, or Safari Technology Preview with WebGPU support.
For detailed WebGPU documentation, see docs/WEBGPU.md.
Binary Size Optimization
Standard Go (~2-10MB)
GOOS=js GOARCH=wasm go build -o main.wasm
TinyGo (~50-400KB)
tinygo build -o main.wasm -target wasm .
Note: TinyGo has some limitations (no recover(), cooperative goroutines).
- Virtual DOM diffing: O(n) time complexity
- Keyed reconciliation: Efficient list updates
- Batched updates: requestAnimationFrame scheduling
- Memory management: Automatic cleanup of event handlers
- Zero allocations: Reusable patch buffers (coming soon)
Development
Building Guix
# Install dependencies
go mod download
# Run tests
go test ./...
# Build CLI
go build -o guix ./cmd/guix
# Install locally
go install ./cmd/guix
Project Structure
guix/
โโโ cmd/guix/ # CLI tool
โโโ pkg/
โ โโโ ast/ # Guix AST definitions
โ โโโ parser/ # Participle-based parser
โ โโโ codegen/ # Go AST code generator
โ โโโ runtime/ # Virtual DOM runtime
โโโ internal/cache/ # Incremental compilation
โโโ examples/ # Example applications
Contributing
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
License
MIT License - see LICENSE for details.
Inspiration
Guix draws inspiration from:
- Vugu - Go virtual DOM and instruction buffer patterns
- Vecty - Go WebAssembly component architecture
- templ - Template parsing and code generation techniques
- React - Component model and virtual DOM concepts
- Elm - Functional architecture and type safety
- Yew - Rust WebAssembly framework with
html! macro for declarative UI
- Dioxus - Rust UI framework with
rsx! macro for component composition
Roadmap
- Component lifecycle hooks (onMount, onUnmount)
- CSS-in-Go styling system
- Server-side rendering (SSR)
- Dev server with hot reload
- Component testing utilities
- Browser DevTools integration
- Keyed fragments optimization
- Async component loading
- Web Components compatibility
- Progressive Web App (PWA) support
FAQ
Why Go for UI?
Go provides strong typing, excellent tooling, and native WebAssembly support. Guix lets you use Go's ecosystem and type safety for building web UIs.
How does it compare to Vugu?
Guix uses a custom DSL (.gx files) with template interpolation, while Vugu uses Go files with HTML comments. Guix also provides automatic option function generation and a more React-like API.
Can I use existing Go packages?
Yes! Guix generates standard Go code, so you can import and use any Go package that works with WebAssembly.
What about browser compatibility?
Guix requires WebAssembly support, which is available in all modern browsers (Chrome 57+, Firefox 52+, Safari 11+, Edge 16+).
Resources
Support
Guix - Making Go a first-class language for web UI development.