DEV Community

Aviral Srivastava
Aviral Srivastava

Posted on

Templating with Go's html/template and text/template

Templating with Go: Mastering html/template and text/template

Introduction:

Templating is a fundamental aspect of modern software development, allowing us to separate data from presentation logic. This separation promotes code reusability, maintainability, and readability. Go, with its emphasis on simplicity and efficiency, offers two powerful built-in packages for templating: html/template and text/template. While both serve the same core purpose – generating textual output based on data – they cater to different needs. html/template is specifically designed for generating HTML and automatically escapes potentially harmful characters, preventing cross-site scripting (XSS) vulnerabilities. text/template, on the other hand, is more generic and suitable for generating any kind of text-based format like configuration files, emails, or even code. This article delves into the intricacies of both packages, exploring their functionalities, advantages, disadvantages, and best practices.

Prerequisites:

Before diving into the code, it's assumed you have a basic understanding of:

  • Go programming language fundamentals (variables, data types, control flow, functions).
  • HTML basics (if you intend to use html/template).

Ensure you have Go installed and configured on your system. You can download it from https://go.dev/dl/.

Advantages of Using Go Templating:

  • Built-in and Standard: html/template and text/template are part of the Go standard library. This means no external dependencies are required, simplifying project management and ensuring long-term stability.
  • Security (for html/template): The automatic escaping provided by html/template significantly reduces the risk of XSS vulnerabilities, making it a secure choice for generating web pages.
  • Simplicity and Readability: Go's templating syntax is relatively straightforward and easy to learn, promoting code clarity and maintainability.
  • Performance: Go's compiled nature ensures that template execution is fast and efficient.
  • Flexibility: Both packages offer features like conditional statements, loops, and function calls within templates, allowing for complex and dynamic output generation.
  • Customizability: You can extend the functionality of the templating engine by defining custom functions accessible within the templates.

Disadvantages of Using Go Templating:

  • Limited Expressiveness: While capable, Go's templating language isn't as expressive as some dedicated templating engines found in other languages.
  • Error Handling: While the packages provide error handling, debugging complex templates can sometimes be challenging.
  • Lack of Inheritance (Directly): True template inheritance, common in some frameworks, isn't directly supported. You typically achieve similar results using template composition.

Core Concepts and Features:

  1. Parsing Templates:

    The first step is to parse the template string or file into a template object. This is done using the template.New() function to create a new template, and then using template.Parse() or template.ParseFiles() to parse the template content.

    package main
    
    import (
        "fmt"
        "html/template"
        "log"
        "os"
    )
    
    func main() {
        tmpl, err := template.New("example").Parse("<h1>Hello, {{.Name}}!</h1>")
        if err != nil {
            log.Fatal(err)
        }
    
        data := struct {
            Name string
        }{
            Name: "World",
        }
    
        err = tmpl.Execute(os.Stdout, data)
        if err != nil {
            log.Fatal(err)
        }
    
        fmt.Println()
    
        // Parsing from a file:
        tmplFile, err := template.ParseFiles("template.html")
        if err != nil {
            log.Fatal(err)
        }
    
        err = tmplFile.Execute(os.Stdout, data)
        if err != nil {
            log.Fatal(err)
        }
    }
    
    
*   `template.New("example")`: Creates a new template with the name "example".  The name is optional and used for accessing named templates in larger systems.
*   `tmpl.Parse("<h1>Hello, {{.Name}}!</h1>")`: Parses the given string as a template.  `{{.Name}}` is a template action.
*   `template.ParseFiles("template.html")`: Parses the contents of the file "template.html" as a template. Create a file named "template.html" with the following content to make this work: `<h1>Hello, {{.Name}}! from file</h1>`
Enter fullscreen mode Exit fullscreen mode
  1. Template Actions:

    Template actions are special directives enclosed in double curly braces {{ ... }} that control the template's behavior. Common actions include:

*   **`{{.FieldName}}`:** Accessing fields of the data passed to the template. The dot (`.`) represents the current data context.
*   **`{{range .Slice}} ... {{end}}`:** Iterating over a slice or array.
*   **`{{if .Condition}} ... {{else}} ... {{end}}`:** Conditional statements.
*   **`{{with .Context}} ... {{end}}`:**  Changing the data context for a section of the template.
*   **`{{template "name" .}}`:** Including another named template.
*   **`{{/* comment */}}`:** Comments within the template.
*   **`{{define "name"}} ... {{end}}`:** Defining a named template.
Enter fullscreen mode Exit fullscreen mode
  1. Executing Templates:

    The Execute() method applies the template to the provided data and writes the output to an io.Writer (e.g., os.Stdout, bytes.Buffer, an HTTP response writer).

    err := tmpl.Execute(os.Stdout, data)
    
*   `os.Stdout` is the standard output (console).
*   `data` is the data structure (struct, map, etc.) that will be used to populate the template.
Enter fullscreen mode Exit fullscreen mode
  1. Data Structures:

    Go templates can work with various data structures, including:

*   **Structs:** Access struct fields directly using `{{.FieldName}}`.
*   **Maps:** Access map values using `{{.KeyName}}`.
*   **Slices/Arrays:** Iterate over slices/arrays using the `range` action.
Enter fullscreen mode Exit fullscreen mode
  1. Functions:
*   **Built-in Functions:** Go templates provide a set of built-in functions like `print`, `printf`, `len`, `index`, `and`, `or`, `not`, `eq`, `ne`, `lt`, `le`, `gt`, `ge`.
*   **Custom Functions:** You can define your own functions and make them available within the template using the `Funcs()` method.
Enter fullscreen mode Exit fullscreen mode
```go
package main

import (
    "fmt"
    "html/template"
    "log"
    "os"
    "strings"
)

func main() {
    funcMap := template.FuncMap{
        "upper": strings.ToUpper,
    }

    tmpl, err := template.New("example").Funcs(funcMap).Parse("<h1>{{.Name | upper}}!</h1>")
    if err != nil {
        log.Fatal(err)
    }

    data := struct {
        Name string
    }{
        Name: "World",
    }

    err = tmpl.Execute(os.Stdout, data)
    if err != nil {
        log.Fatal(err)
    }
}
```
Enter fullscreen mode Exit fullscreen mode
*   `template.FuncMap` is a map that associates function names with Go functions.
*   `tmpl.Funcs(funcMap)` adds the custom functions to the template.
*   `{{.Name | upper}}` pipes the value of `.Name` to the `upper` function.
Enter fullscreen mode Exit fullscreen mode
  1. Template Composition:

    Instead of direct inheritance, Go encourages template composition using the {{template "name" .}} action. This allows you to define reusable template fragments and include them in other templates.

    {{define "header"}}
    <h1>My Website</h1>
    {{end}}
    
    {{define "footer"}}
    <p>&copy; 2023</p>
    {{end}}
    
    {{define "page"}}
    {{template "header" .}}
    <p>{{.Content}}</p>
    {{template "footer" .}}
    {{end}}
    

    Then execute the "page" template.

  2. html/template vs. text/template:

    The key difference is that html/template automatically escapes HTML entities to prevent XSS attacks. It provides contextual auto-escaping, meaning it escapes differently depending on where the data is being inserted into the HTML. text/template does no escaping. Choose html/template when generating HTML and text/template for other text-based formats where escaping is not required or should be handled manually.

Practical Example (Generating an HTML Table):

package main

import (
    "fmt"
    "html/template"
    "log"
    "os"
)

type User struct {
    ID    int
    Name  string
    Email string
}

func main() {
    users := []User{
        {ID: 1, Name: "Alice", Email: "alice@example.com"},
        {ID: 2, Name: "Bob", Email: "bob@example.com"},
    }

    tmpl, err := template.New("users").Parse(`
    <table>
        <thead>
            <tr>
                <th>ID</th>
                <th>Name</th>
                <th>Email</th>
            </tr>
        </thead>
        <tbody>
            {{range .}}
            <tr>
                <td>{{.ID}}</td>
                <td>{{.Name}}</td>
                <td>{{.Email}}</td>
            </tr>
            {{end}}
        </tbody>
    </table>
    `)

    if err != nil {
        log.Fatal(err)
    }

    err = tmpl.Execute(os.Stdout, users)
    if err != nil {
        log.Fatal(err)
    }
}
Enter fullscreen mode Exit fullscreen mode

Conclusion:

Go's html/template and text/template packages provide a robust and efficient way to generate dynamic textual content. Choosing the right package based on the output format and security requirements is crucial. By mastering the core concepts and features, you can leverage the power of Go templating to create maintainable, secure, and performant applications. Remember to prioritize security when generating HTML by using html/template and carefully consider the need for custom functions and template composition to enhance code reusability and readability.

Top comments (1)

Collapse
 
roshan_sharma_7deae5e0742 profile image
roshan sharma

Awesome guide! Clear, practical, and very helpful for mastering Go templating.
How would you use html/template to safely display user-generated content in a web page?