HTX 🧩
HTX is a simple, composable, and testable Go HTML template extension for the Go html/template package.
Features
- base layouts
- partial includes
- inline string rendering
- template caching
- custom template functions
- glob pattern loading
- easy to use and test
Installation
go get github.com/shayanderson/htx
Basic Usage
func main() {
e := htx.New(htx.Config{
Base: "layout/base",
Extension: ".html",
Load: []string{"partials/*.html"},
Root: "app/view",
})
var buf bytes.Buffer
err := e.Render(&buf, "index", map[string]any{
"name": "World",
})
if err != nil {
panic(err)
}
fmt.Println(buf.String())
}
Engine Configuration
All engine configuration:
Base - base layout template name (without extension), e.g. layout/base
DisableCache - disable template caching (default: false)
Extension - template file extension, will be applied to all templates without file extensions (default: .html)
Funcs - custom template functions (see Template Functions)
Load - additional partial templates to load (supports glob patterns), e.g. partials/*.html
Root - root directory for templates (default: current working directory)
Example Templates
app/view/layout/base.html - base layout with header include and content block
<!DOCTYPE html>
<html>
<head>
<title>{{ .title }}</title>
</head>
<body>
{{ include "layout/header" . }}
<!-- content defined in other templates -->
{{ block "content" . }}{{ end }}
</body>
</html>
app/view/layout/header.html - header partial
<header>header</header>
app/view/partials/banner.html - banner partial, loaded via config Load option
<div class="banner">Welcome, {{ .name }}!</div>
app/view/index.html - main index template
<!-- content block will be injected into base layout -->
{{ define "content" }}
<h1>Hello, {{ .name }}!</h1>
{{ include "banner" . }} {{ end }}
Rendering a Template String
You can render a template from a string instead of a file with the RenderString method.
tpl := `
{{ define "content" }}
<h1>Hello, {{ .name }}!</h1>
{{ include "banner" . }}
{{ end }}
`
var buf bytes.Buffer
err := e.RenderString(&buf, tpl, map[string]any{
"name": "World",
})
if err != nil {
panic(err)
}
fmt.Println(buf.String())
Render Options
You can override the engine config options per render call via the Options struct.
err := e.Render(&buf, "index", data, htx.Options{
// disable base layout for this render
DisableBase: true,
// add custom template functions for this render
Funcs: template.FuncMap{
"myFunc": func() string { return "myFuncVal" },
},
// load additional partials for this render
Load: []string{"other_partials/*.html"},
})
All render options:
DisableBase - disable base layout for this render (default: false)
DisableCache - disable template caching for this render (default: false)
Funcs - additional custom template functions for this render (see Template Functions)
Load - additional partial templates to load for this render (supports glob patterns)
Template Functions
You can add custom template functions via the Funcs field in the Config struct or via the Options struct when rendering.
Default functions:
get - get a value from the engine store by key, e.g. {{ get "year" }}
- example set store value:
e.Store("year", 2025)
include - include another template partial, e.g. {{ include "header" . }}
list - creates a list
- for iteration:
{{ range list 1 2 3 }}{{ . }}{{ end }}
- as a template variable:
{{ template "myTemplate" (list 1 2 3) }}
map - creates a map
- for use in templates:
{{ $m := map "key1" "val1" "key2" "val2" }}{{ $m.key1 }}
- as a template variable:
{{ template "myTemplate" (map "key1" "val1" "key2" "val2") }}
- can use with
list as value: {{ template "myTemplate" (map "key1" (list 1 2 3) "key2" "val2") }}
Default Engine
You can set up a default engine to use throughout your app.
e := htx.New(htx.Config{
// ...
})
htx.SetDefault(e)
// later in your code
err := htx.Render(&buf, "index", data)
// or access engine directly
htx.Default()
Testing
Test templates exist in testdata/ and can be tested with:
make test