Go templates are made to be very adaptable. This allows for many different approaches for template architectures. This guide builds a simple and extensible parent/child template system. If you are coming from a React ecosystem, you might like Templ.
Here is what the most basic Go template looks like:
package main
import (
"html/template"
"net/http"
)
func main() {
// Define a template
tmpl := template.Must(template.New("hello").Parse(`
<!DOCTYPE html>
<html>
<body>
<h1>Hello, {{.}}!</h1>
</body>
</html>
`))
// Make a router
mux := http.NewServeMux()
// Define a handler to run if a request to the webserver
mux.HandleFunc(" GET /hello", getHello)
http.ListenAndServe(":8000", mux)
}
func homeHandler(w http.ResponseWriter, r *http.Request) {
tmpl.Execute(w, "World") // You can change "World" to any string
}
The go template documentation lists all of the different things you can do in templates.
Here is a production ready example from jaxlo.net. Notice that the errors tell the end user that something went wrong. But the exact error ,with potential sensitive data, is sent to the terminal/log.
func ServeTemplates(w http.ResponseWriter, data any, templatePaths []string) error {
// If there is a base template, it should be first in templatePaths
t, err := template.ParseFiles(templatePaths...)
if err != nil {
http.Error(w, "Error parsing template", http.StatusInternalServerError) // This tells the user something went wrong
log.Println("Error parsing template:", err)
return err
}
err = t.Execute(w, data)
if err != nil {
http.Error(w, "Error executing template", http.StatusInternalServerError)
log.Println("Error executing template:", err)
return err
}
return nil
}
This is what this looks like when used in a handler with multiple variables.
func GetPosts(w http.ResponseWriter, r *http.Request) {
posts := Posts() // Defined elsewhere. This can also be a database lookup
ServeTemplates(w, map[string]any{"r": r, "posts": posts}, []string{"views/base.html", "views/home.html"})
}
This is what the HTML template looks like for posts:
<h2>Posts</h2>
{{range .posts}}
<div>
<h3>
<a href="/project/{{.Codename}}">{{.Title}}</a>
</h3>
<p>{{.Date}} |</span> {{.Subtitle}}</p>
</div>
{{end}}
And this is what it looks like to apply a particular css class when the URL path matches one in the nav bar:
<nav>
<a href="/" {{if eq .r.URL.Path "/"}}class="nav-active"{{end}}>Home</a>
<a href="/books" {{if eq .r.URL.Path "/books"}}class="nav-active"{{end}}>Book Reviews</a>
<a href="/sites" {{if eq .r.URL.Path "/sites"}}class="nav-active"{{end}}>Sites</a>
<a href="/guestbook" {{if eq .r.URL.Path "/guestbook"}}class="nav-active"{{end}}>Guestbook</a>
</nav>