text/template and html/template
text/template and html/template
Overview
The template packages render text with actions ({{...}}). Use html/template for browser HTML (context-aware escaping). Use text/template for configs, emails-as-text, code generation, and non-HTML output.
Web-focused usage: Templating and rendering.
When to use which
| Package | Escaping | Use |
|---|---|---|
html/template |
Yes (HTML/JS/CSS/URL contexts) | Web pages, HTML emails |
text/template |
No | Nginx configs, Markdown, codegen, plain text |
Serving user data as HTML with text/template is an XSS footgun.
Core loop: parse once, execute many
import "html/template"
tmpl, err := template.New("hi").Parse(`Hello, {{.Name}}!`)
if err != nil {
log.Fatal(err)
}
err = tmpl.Execute(os.Stdout, struct{ Name string }{"Gopher"})
// Hello, Gopher!From files:
tmpl, err := template.ParseFiles("layout.tmpl", "page.tmpl")
// or template.ParseFS(fsys, "templates/*.tmpl")Actions you will use constantly
{{.Field}} // print field
{{if .OK}}yes{{else}}no{{end}}
{{range .Items}}{{.}}{{end}}
{{with .User}}{{.Email}}{{end}}
{{/* comment */}}Pipelines:
{{.Title | printf "%q" | html}}Define and execute named templates
{{define "layout"}}
<html><body>{{template "content" .}}</body></html>
{{end}}
{{define "content"}}
<h1>{{.Title}}</h1>
{{end}}err := tmpl.ExecuteTemplate(w, "layout", data)FuncMap
funcs := template.FuncMap{
"upper": strings.ToUpper,
"cents": func(n int) string {
return fmt.Sprintf("$%.2f", float64(n)/100)
},
}
tmpl, err := template.New("price").Funcs(funcs).Parse(`{{cents .Price}} {{upper .Title}}`)Register Funcs before Parse. Only expose pure, trusted helpers.
html/template safety
// Auto-escaped:
// {{.Comment}} from user → <script>...
// Escape hatch (trusted HTML only):
type page struct {
Body template.HTML
}| Type | Meaning |
|---|---|
template.HTML |
Trusted HTML fragment |
template.JS |
Trusted JS |
template.CSS |
Trusted CSS |
template.URL |
Trusted URL |
Never cast raw request input to these types.
Whitespace and formatting
{{- /* trim left */ -}}
{{.Name -}} {{/* trim right */}}- next to {{ / }} trims adjacent whitespace—useful for tidy HTML or generated code.
text/template for codegen
const stub = `package {{.Pkg}}
func {{.Name}}() string { return {{printf "%q" .Msg}} }
`
t := template.Must(template.New("stub").Parse(stub))
var buf bytes.Buffer
_ = t.Execute(&buf, struct {
Pkg, Name, Msg string
}{"demo", "Hello", "hi"})
// gofmt the result in real toolsCommon errors
| Error | Cause |
|---|---|
complete and no parse result |
Empty parse / wrong name |
no template "X" |
ExecuteTemplate name mismatch |
Missing data prints <no value> |
Field name wrong or nil pipeline |
| Func not defined | Funcs registered after Parse |
Rules of thumb
| Do | Don’t |
|---|---|
| Parse at startup | Parse templates on every request |
html/template for HTML |
Build HTML with fmt + user strings |
| Pass view structs | Pass *sql.DB into templates |
template.Must only at init |
Must in request paths (panic under load) |
Try next
- Parse a layout + content from
embed.FSand execute both HTML and a JSON-free page. - Add a
FuncMapthat formatstime.Timeas RFC3339. - Intentionally inject
<script>via data and confirmhtml/templateescapes it.