Working with Templates in Go
Go provides two main packages for template processing: text/template
for general text templates and html/template
for safe HTML generation. This guide covers both packages and their features.
Basic Templates
Text Templates
func basicTemplate() error {
// Define template
const tmpl = `Hello, {{.Name}}!
Age: {{.Age}}
Email: {{.Email}}`
// Parse template
t, err := template.New("greeting").Parse(tmpl)
if err != nil {
return fmt.Errorf("parsing template: %w", err)
}
// Execute template
data := struct {
Name string
Age int
Email string
}{
Name: "John Doe",
Age: 30,
Email: "[email protected]",
}
if err := t.Execute(os.Stdout, data); err != nil {
return fmt.Errorf("executing template: %w", err)
}
return nil
}
HTML Templates
func htmlTemplate() error {
// Define template
const tmpl = `
<!DOCTYPE html>
<html>
<head>
<title>{{.Title}}</title>
</head>
<body>
<h1>{{.Header}}</h1>
<p>{{.Content}}</p>
</body>
</html>`
// Parse template
t, err := template.New("page").Parse(tmpl)
if err != nil {
return fmt.Errorf("parsing template: %w", err)
}
// Execute template
data := struct {
Title string
Header string
Content string
}{
Title: "Welcome",
Header: "Hello, World!",
Content: "This is a sample page.",
}
if err := t.Execute(os.Stdout, data); err != nil {
return fmt.Errorf("executing template: %w", err)
}
return nil
}
Template Features
Control Structures
const tmpl = `
{{if .Success}}
Operation completed successfully!
{{else}}
Error: {{.Error}}
{{end}}
{{range .Items}}
- {{.Name}}: {{.Price}}
{{end}}
{{with .User}}
Name: {{.Name}}
Email: {{.Email}}
{{end}}`
Functions and Pipelines
const tmpl = `
{{.Name | printf "Name: %s"}}
{{.Price | printf "%.2f"}}
{{.Description | truncate 50}}
{{.Date | formatDate "2006-01-02"}}`
func setupTemplate() *template.Template {
funcMap := template.FuncMap{
"truncate": func(s string, n int) string {
if len(s) <= n {
return s
}
return s[:n] + "..."
},
"formatDate": func(layout string, date time.Time) string {
return date.Format(layout)
},
}
return template.New("example").Funcs(funcMap)
}
Template Files
Loading Template Files
func loadTemplates() error {
// Load single template
t, err := template.ParseFiles("templates/page.html")
if err != nil {
return fmt.Errorf("parsing template: %w", err)
}
// Load multiple templates
t, err = template.ParseFiles(
"templates/base.html",
"templates/header.html",
"templates/footer.html",
)
if err != nil {
return fmt.Errorf("parsing templates: %w", err)
}
// Load all templates from directory
t, err = template.ParseGlob("templates/*.html")
if err != nil {
return fmt.Errorf("parsing templates: %w", err)
}
return nil
}
Template Inheritance
<!-- base.html -->
<!DOCTYPE html>
<html>
<head>
<title>{{template "title" .}}</title>
</head>
<body>
<header>{{template "header" .}}</header>
<main>{{template "content" .}}</main>
<footer>{{template "footer" .}}</footer>
</body>
</html>
<!-- page.html -->
{{define "title"}}Welcome{{end}}
{{define "header"}}
<h1>Welcome to our site</h1>
{{end}}
{{define "content"}}
<article>
<h2>{{.Title}}</h2>
<p>{{.Content}}</p>
</article>
{{end}}
{{define "footer"}}
<p>© 2024 Our Company</p>
{{end}}
Advanced Features
Custom Functions
func setupFunctions() template.FuncMap {
return template.FuncMap{
// String operations
"lower": strings.ToLower,
"upper": strings.ToUpper,
"title": strings.Title,
// Date formatting
"formatDate": func(t time.Time) string {
return t.Format("2006-01-02")
},
// Custom logic
"increment": func(n int) int {
return n + 1
},
// Conditional operations
"ifelse": func(cond bool, v1, v2 interface{}) interface{} {
if cond {
return v1
}
return v2
},
}
}
Nested Templates
func nestedTemplates() error {
const (
master = `
Name: {{template "user" .}}
Address: {{template "address" .}}`
userTmpl = `{{define "user"}}
{{.FirstName}} {{.LastName}}
{{end}}`
addressTmpl = `{{define "address"}}
{{.Street}}
{{.City}}, {{.State}} {{.ZIP}}
{{end}}`
)
// Parse all templates
t, err := template.New("master").Parse(master)
if err != nil {
return err
}
_, err = t.Parse(userTmpl)
if err != nil {
return err
}
_, err = t.Parse(addressTmpl)
if err != nil {
return err
}
// Execute template
data := struct {
FirstName string
LastName string
Street string
City string
State string
ZIP string
}{
FirstName: "John",
LastName: "Doe",
Street: "123 Main St",
City: "Anytown",
State: "ST",
ZIP: "12345",
}
return t.Execute(os.Stdout, data)
}
Best Practices
Use HTML Templates for Web Content
import "html/template" func renderPage(w http.ResponseWriter, data interface{}) error { t := template.Must(template.ParseFiles("templates/page.html")) return t.Execute(w, data) }
Cache Templates
var templates = template.Must(template.ParseGlob("templates/*.html")) func renderTemplate(w http.ResponseWriter, name string, data interface{}) error { return templates.ExecuteTemplate(w, name, data) }
Handle Errors
func handleTemplate(w http.ResponseWriter, data interface{}) { err := templates.ExecuteTemplate(w, "page.html", data) if err != nil { http.Error(w, "Template error", http.StatusInternalServerError) log.Printf("template error: %v", err) } }
Use Context
type PageData struct { Title string Content string User *User IsAdmin bool } func buildPageData(r *http.Request) PageData { user := getUserFromContext(r.Context()) return PageData{ Title: "Welcome", Content: "Page content", User: user, IsAdmin: user.IsAdmin, } }
Security Considerations
Auto-Escaping
// HTML template automatically escapes content const tmpl = `<p>{{.UserInput}}</p>`
Trusted Content
// Mark trusted HTML as safe template.HTML("<strong>Bold text</strong>") // Mark trusted URLs as safe template.URL("https://example.com")
Content Security Policy
func setSecurityHeaders(w http.ResponseWriter) { w.Header().Set("Content-Security-Policy", "default-src 'self'; script-src 'self'") }
Next Steps
- Learn about HTTP Server
- Explore JSON Processing
- Study Regular Expressions