Skip to content

Instantly share code, notes, and snippets.

@hawx
Last active March 13, 2025 11:25
Show Gist options
  • Save hawx/b175e6cfe356b8e8b84bba178790c04f to your computer and use it in GitHub Desktop.
Save hawx/b175e6cfe356b8e8b84bba178790c04f to your computer and use it in GitHub Desktop.
Nested Go templating
// This design would allow us to have 1- or 2-depth nested templates.
//
// Everything uses rootTmpl, and everything has access to all
// componentTmpls. But each combinedTmpl only uses the named template in
// layoutTmpls, except `page-*` which are defined under rootTmpl only.
package main
import (
"fmt"
"os"
"strings"
"text/template"
)
func main() {
rootTmpl := template.Must(template.New("").Parse(`Page > {{ template "root" }} {{ template "footer" }}`))
componentTmpls := []*template.Template{
template.Must(template.New("header").Parse("<Header>")),
template.Must(template.New("footer").Parse("<Footer>")),
template.Must(template.New("some-component").Parse("<Some>")),
}
for _, tmpl := range componentTmpls {
rootTmpl.AddParseTree(tmpl.Name(), tmpl.Tree)
}
layoutTmpls := map[string]*template.Template{
"fixtures": template.Must(template.New("").Parse(`Fixtures > {{ template "fixtures" }}`)),
"guidance": template.Must(template.New("").Parse(`{{ template "header" }} Guidance > {{ template "guidance" }}`)),
}
tmpls := map[string]*template.Template{
"page-basic": template.Must(template.New("").Parse(`Basic`)),
"page-superbasic": template.Must(template.New("").Parse(`Super basic`)),
"fixtures-donor": template.Must(template.New("").Parse(`Donor`)),
"fixtures-attorney": template.Must(template.New("").Parse(`Attorney`)),
"guidance-some": template.Must(template.New("").Parse(`{{ template "some-component" }} Some`)),
"guidance-other": template.Must(template.New("").Parse(`Other`)),
}
combinedTmpls := map[string]*template.Template{}
for name, tmpl := range tmpls {
root, _ := rootTmpl.Clone()
if layoutName := strings.Split(name, "-")[0]; layoutName == "page" {
root.AddParseTree("root", tmpl.Tree)
} else {
root.AddParseTree("root", layoutTmpls[layoutName].Tree)
root.AddParseTree(layoutName, tmpl.Tree)
}
combinedTmpls[name] = root
}
for k, v := range combinedTmpls {
fmt.Print(k + ": ")
v.Execute(os.Stdout, nil)
fmt.Println("")
}
}
fixtures-attorney: Page > Fixtures > Attorney <Footer>
guidance-some: Page > <Header> Guidance > <Some> Some <Footer>
guidance-other: Page > <Header> Guidance > Other <Footer>
page-basic: Page > Basic <Footer>
page-superbasic: Page > Super basic <Footer>
fixtures-donor: Page > Fixtures > Donor <Footer>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment