Skip to content

Minimal HTML templating system for Go with fast, composable, and streamable rendering using pure Go functions.

License

Notifications You must be signed in to change notification settings

jlucasnsilva/mx

Repository files navigation

mx — Streamable HTML Templates in Go

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!"))
	})
}

🚀 Features

  • ⚡ 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

📦 Installation

go get github.com/jlucasnsilva/mx

📐 Usage

Define a Page

func HelloPage(n *mx.Node) {
	n.Div(mx.Class("greeting"), func(n *mx.Node) {
		n.H1(nil, mx.Text("Hello, world!"))
	})
}

Render It

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)
	}
}

💻 Dev Mode (Pretty Output + Profiling)

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.


🧩 Create Components

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)
		})
	}
}

🧪 Testing

Test rendering via strings.Builder:

var b strings.Builder
node := &mx.Node{Writer: &b}
HomePage(node)
assert.Contains(t, b.String(), "<div")

🪵 Debugging / Error Handling

if err := mx.Error(node); err != nil {
	log.Println("render error:", err)
}

🧱 WrapEach Example

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 — CLI to convert HTML → mx Go functions

mxgen is a simple CLI tool that reads an HTML file and generates a Go function using the mx template system.

✨ Usage

go run ./mxgen input.html ComponentName

📤 Example

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.

🧰 Install mxgen as a CLI tool

You can compile mxgen as a standalone binary and use it from the terminal.

🔨 Build and Install

go install ./mxgen

This will install the mxgen binary into your $GOBIN (e.g. $HOME/go/bin/mxgen).

🚀 Usage

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.

About

Minimal HTML templating system for Go with fast, composable, and streamable rendering using pure Go functions.

Topics

Resources

License

Stars

Watchers

Forks