mx
is a minimal, type-safe, and composable HTML templating engine for Go. Write HTML directly in Go using functions — no string templating, no runtime parsing, and no magic.
func HomePage(n *mx.Node) {
n.Div(mx.S(`class="container"`), func(n *mx.Node) {
n.H1(nil, mx.Text("Welcome!"))
})
}
- ⚡ Streamed rendering (
io.Writer
) - ✅ Safe HTML escaping by default
- 🧱 Typed HTML elements
- 🧩 Composable components (just Go functions)
- 🧪 Dev mode: pretty output + auto-profiling with
data-node
- 🎯 Zero dependencies
go get github.com/jlucasnsilva/mx
func HelloPage(n *mx.Node) {
n.Div(mx.Class("greeting"), func(n *mx.Node) {
n.H1(nil, mx.Text("Hello, world!"))
})
}
func handler(w http.ResponseWriter, r *http.Request) {
node := &mx.Node{Writer: w}
HelloPage(node)
if err := node.Err(); err != nil {
http.Error(w, err.Error(), 500)
}
}
node := &mx.Node{Writer: os.Stdout, DevMode: true}
HelloPage(node)
Outputs:
<div class="greeting" data-node="div">
<h1 data-node="h1">Hello, world!</h1>
</div>
Use data-node
attributes for tracing or Chrome dev tools filtering.
func Card(title string, body func(*mx.Node)) func(*mx.Node) {
return func(n *mx.Node) {
n.Div(mx.Class("card"), func(n *mx.Node) {
n.H2(nil, mx.Text(title))
body(n)
})
}
}
Test rendering via strings.Builder
:
var b strings.Builder
node := &mx.Node{Writer: &b}
HomePage(node)
assert.Contains(t, b.String(), "<div")
if err := mx.Error(node); err != nil {
log.Println("render error:", err)
}
Wrap each child node in a component-defined wrapper:
func Grid(n *mx.Node, children func(*mx.Node)) {
n.Div(mx.Class("grid"), func(n *mx.Node) {
mx.WrapEach(n, func(n *mx.Node, content func(*mx.Node)) {
n.Div(mx.Class("grid-item"), content)
}, children)
})
}
Use like this:
Grid(n, func(n *mx.Node) {
n.Text("Item 1")
n.Text("Item 2")
n.Text("Item 3")
})
Output:
<div class="grid">
<div class="grid-item">Item 1</div>
<div class="grid-item">Item 2</div>
<div class="grid-item">Item 3</div>
</div>
mxgen
is a simple CLI tool that reads an HTML file and generates a Go function using the mx
template system.
go run ./mxgen input.html ComponentName
Given this hero.html
:
<div class="hero">
<h1 id="title">Welcome!</h1>
</div>
Running:
go run ./mxgen hero.html Hero
Outputs:
func Hero(n *mx.Node) {
n.Div(mx.M{"class": "hero"}, func(n *mx.Node) {
n.H1(mx.M{"id": "title"}, func(n *mx.Node) {
n.Text("Welcome!")
})
})
}
- All tags and attributes are supported.
- Output is written to
stdout
. - Can be used in CI, generators, or quick prototyping.
You can compile mxgen
as a standalone binary and use it from the terminal.
go install ./mxgen
This will install the mxgen
binary into your $GOBIN
(e.g. $HOME/go/bin/mxgen
).
mxgen -in=input.html -name=ComponentName
It will print:
func ComponentName(n *mx.Node) {
// converted HTML
}
Use it in CI pipelines, scaffolding tools, or rapid prototyping.