ht is a hyper-minimal, functional HTML builder for Go. It provides a clean, type-safe API for constructing standard golang.org/x/net/html AST nodes.
Warning
DO NOT go get THIS PACKAGE!
This is not a dependency to add to your go.mod. It is a minimal (~300 lines) foundation designed to be copied, stolen, and modified.
Copy nodes.go and attrs.go directly into your project. Tweak them. Add your own project-specific helpers (like custom SVG components or specific JavaScript bindings). Own your HTML builder.
- Functional Components: Build HTML exactly how you build Go code. Components are just standard Go functions that return
*html.Node. No more wrestling with template context or inheritance hierarchies. - Standard Library AST: This isn't a custom virtual DOM. It constructs the exact same
*html.NodeAST that the Go standard library uses to parse HTML.html.Rendersimply serializes it. - Resilient Rendering: In standard templates, an error halfway through rendering crashes the HTTP response and leaves broken HTML. With
ht, AST building and network serialization are separate. If a component fails to build, you can catch the error and safely return an "Error Node" (like a fallback UI) without breaking the rest of your layout. - First-Class HTMX & Alpine Support: Easily create hypermedia-driven SPAs with included attribute helpers (
HxPost,HxSwapOob,XData,XOn) that feel native to Go.
We provide two comprehensive examples in the examples/ directory to show how to use ht:
todo/: A fully functional, highly interactive Todo application utilizing HTMX, Alpine.js, and DaisyUI. Shows how to use partials, out-of-band swaps (hx-swap-oob), and local component state.template/: Demonstrates how to seamlessly interop with legacytext/templateorhtml/templatecode. Shows how to safely execute templates intoRawnodes and embed them directly into anhtAST.
package main
import (
"os"
"golang.org/x/net/html"
// Import your local, copied package
. "yourproject/internal/ht"
)
func main() {
page := Document(
Doctype("html"),
Html(
Lang("en"),
Head(
Title(Text("My App")),
Script(Src("https://unpkg.com/htmx.org@2.0.4")),
),
Body(
Class("bg-base-300"),
H1(Class("text-2xl font-bold"), Text("Hello, World")),
Button(
Class("btn btn-primary"),
HxPost("/clicked"),
Text("Click Me"),
),
),
),
)
_ = html.Render(os.Stdout, page)
}- Naming Conflicts: Some attribute helpers are suffixed with
Attr(e.g.,LabelAttr,StyleAttr,TitleAttr) to avoid naming conflicts with the HTML element constructors (Label,Style,Title). - Raw HTML: Use
Raw("<br>")to inject unescaped HTML strings. Only pass trusted content toRaw. UseText("Hello")for regular strings; it will be automatically escaped by the renderer. - Node Detachment: If you pass an existing
*html.Nodeas a child, it is appended using standardnode.AppendChildsemantics. The child node MUST be detached (Parent == nil,PrevSibling == nil,NextSibling == nil) or the Go standard library will panic.