JSX-like syntax for Go. Write components with familiar JSX patterns, get type-safe Go code.
// button.gox
package ui
type ButtonProps struct {
Label string
Disabled bool
}
func Button(props ButtonProps) gox.VNode {
return <button disabled={props.Disabled}>
{props.Label}
</button>
}brew tap germtb/tap
brew install goxgo install github.com/germtb/gox/cmd/gox@latestDownload the latest release from GitHub Releases.
# macOS (Apple Silicon)
curl -sSL https://github.com/germtb/gox/releases/latest/download/gox_darwin_arm64.tar.gz | tar xz
sudo mv gox /usr/local/bin/
# macOS (Intel)
curl -sSL https://github.com/germtb/gox/releases/latest/download/gox_darwin_amd64.tar.gz | tar xz
sudo mv gox /usr/local/bin/
# Linux (amd64)
curl -sSL https://github.com/germtb/gox/releases/latest/download/gox_linux_amd64.tar.gz | tar xz
sudo mv gox /usr/local/bin/- Create a
.goxfile:
// hello.gox
package main
import "github.com/germtb/gox"
func Hello() gox.VNode {
return <div>
<h1>Hello, World!</h1>
</div>
}- Run your project:
gox run .That's it. Gox handles code generation automatically.
| Command | Description |
|---|---|
gox run [args] |
Generate and run with go run |
gox build [args] |
Generate and build with go build |
gox generate [path] |
Generate .go files from .gox files |
gox fmt [path] |
Format .gox files |
gox lsp |
Start LSP server (for IDE integration) |
gox version |
Print version |
gox help |
Show help |
# Run a project
gox run ./cmd/myapp
# Build with output
gox build -o myapp ./cmd/myapp
# Generate all .gox files recursively
gox generate ./...
# Format and write changes
gox fmt -w ./...
# List files that need formatting
gox fmt -l .Gox transforms .gox files into standard Go code:
button.gox → gox generate → button_gox.go
The generated code uses the gox.VNode type and is fully type-checked by the Go compiler.
If your project has .gox files, use gox commands:
gox run . # instead of go run .
gox build . # instead of go build .If your project is pure Go (even with gox dependencies), use standard Go:
go run . # works fine
go build . # works fineThis is because gox libraries ship with pre-generated .go files.
If you're publishing a library that uses gox:
- Commit generated files - Remove
*_gox.gofrom.gitignore - Ship both
.goxand*_gox.go- Consumers get working Go code
This allows consumers to go get your library without needing gox installed.
your-library/
├── component.gox # Source (for contributors)
├── component_gox.go # Generated (for consumers)
└── go.mod
| Approach | Pros | Cons |
|---|---|---|
| Commit generated files | go get just works |
Generated code in git |
| Gitignore generated files | Clean repo | Breaks go get, forces gox on consumers |
Most code generation tools (protobuf, sqlc, ent) recommend committing generated files for the same reason.
If you're building an application (not a library):
- Gitignore generated files - Keep your repo clean
- Use
gox run/gox build- Handles generation automatically - Add to CI - Run
gox buildin your build pipeline
Example .gitignore for applications:
*_gox.go
*_gox_test.go
*_gox.go.map
*_gox_test.go.mapComponents with typed props get compile-time type checking:
type ButtonProps struct {
Label string
Disabled bool
OnClick func()
}
func Button(props ButtonProps) gox.VNode {
return <button disabled={props.Disabled} onClick={props.OnClick}>
{props.Label}
</button>
}
// Usage - props are type-checked!
func App() gox.VNode {
return <Button label="Submit" disabled={false} />
}The Go compiler will catch:
- Missing required props
- Wrong prop types
- Typos in prop names
Install the VS Code extension for:
- Syntax highlighting
- Format on save
- Go-to-definition (works across
.goxand.gofiles) - Error diagnostics
cd vscode-gox
npm install
npm run compile
# Then: "Developer: Install Extension from Location..."Configure in .vscode/settings.json:
{
"files.associations": {
"*.gox": "gox"
},
"[gox]": {
"editor.formatOnSave": true
}
}Gox generates source maps (.map files) that remap errors from generated code back to your .gox source:
button_gox.go:42:5: undefined: foo
↓ (remapped)
button.gox:15:5: undefined: foo
This works automatically with gox run and gox build.
your-project/
├── cmd/
│ └── app/
│ └── main.gox # Entry point with JSX
├── ui/
│ ├── button.gox # Component
│ ├── button_gox.go # Generated (gitignore for apps)
│ └── header.gox # Component
├── go.mod
└── .gitignore
No. If you commit the generated *_gox.go files, consumers use standard go get and go build. They never need to know gox exists.
Yes. They live side by side in the same package. .go files can import and use components defined in .gox files (via the generated code).
The runtime is minimal - just the VNode type and helper functions. There's no virtual DOM diffing or complex runtime. Generated code is straightforward Go.
Gox generates a tree of gox.VNode values. You provide a renderer for your target:
- HTML: Walk the tree and output HTML strings
- Terminal: Use a library like bubbletea or lipgloss
- Testing: Inspect the tree directly
See demo/app.gox for a terminal renderer example.
MIT