Jinja2 for Go? Meet Quicktemplate — and Ditch html/template for Good

Leapcell: The Best of Serverless Web Hosting Quicktemplate Template Library Usage Guide Recently, when organizing the code of our project, I found that the code for many activities was very similar in structure and the functions it provided. To facilitate future development, I spent some time writing a tool for generating code frameworks to minimize repetitive work. The code itself is not complex and is closely related to the project code, so I won't go into details here. During this process, I found that the Go standard template libraries text/template and html/template were rather restrictive and inconvenient to use. I learned from GitHub about the third-party template library quicktemplate, which is powerful, has a simple syntax, and is easy to use. Today, we're going to introduce quicktemplate. Quick Start The code in this article uses Go Modules. Initialize the Project First, create the code directory and initialize it: $ mkdir quicktemplate && cd quicktemplate $ go mod init github.com/leapcell/quicktemplate Install Dependencies quicktemplate will convert the template code we write into Go language code. Therefore, we need to install the quicktemplate package and a compiler named qtc: $ go get -u github.com/valyala/quicktemplate $ go get -u github.com/valyala/quicktemplate/qtc Write the Template File First, we need to write a template file in the quicktemplate format. The template file by default has the .qtpl extension. Here, I've written a simple template file greeting.qtpl: All text outside function is treated as comments. {% func Greeting(name string, count int) %} {% for i := 0; i < count; i++ %} Hello, {%s name %} {% endfor %} {% endfunc %} Template Syntax Explanation The template syntax is very simple. We just need to briefly understand the following two points: Templates are in units of functions. Functions can accept parameters of any type and quantity, and these parameters can be used within the function. All text outside the function is a comment, and the qtc compiler will ignore the comments. The content within the function, except for the syntax structure, will be output as-is in the rendered text, including spaces and line breaks. Generate and Use the Template Save greeting.qtpl to the templates directory, and then execute the qtc command. This command will generate the corresponding Go file greeting.qtpl.go, with the package name templates. Now, we can use this template: package main import ( "fmt" "github.com/leapcell/quicktemplate/get-started/templates" ) func main() { fmt.Println(templates.Greeting("leapcell", 5)) } Run the Code Call the template function, pass in the parameters, and return the rendered text: $ go run . Output result: Hello, leapcell Hello, leapcell Hello, leapcell Hello, leapcell Hello, leapcell {%s name %} performs text replacement, and {% for %} loops to generate repeated text. Multiple spaces and line breaks appear in the output because, except for the syntax structure within the function, other content will be retained as-is, including spaces and line breaks. Notes Since quicktemplate converts templates into Go code for use, if the template is modified, the qtc command must be executed first to regenerate the Go code; otherwise, the modifications will not take effect. Syntax Structure quicktemplate supports common Go syntax structures, such as if, for, func, import, return. And the way of writing is not much different from writing Go code directly, with almost no learning cost. However, when using these syntaxes in templates, they need to be enclosed in {% and %}, and if, for, etc. need to add endif, endfor to clearly indicate the end. Variables Above, we've already seen how to render the passed-in parameter name using {%s name %}. Since name is of the string type, s is used after {% to specify the type. quicktemplate also supports other types of values: Integer: {%d int %}, {%dl int64 %}, {%dul uint64 %}. Floating point number: {%f float %}. You can also set the output precision, using {%f.precision float %}. For example, {%f.2 1.2345 %} outputs 1.23. Byte slice ([]byte): {%z bytes %}. String: {%q str %} or byte slice: {%qz bytes %}. Quotes are escaped as ". String: {%j str %} or byte slice: {%jz bytes %}. There are no quotes. URL encoding: {%u str %}, {%uz bytes %}. {%v anything %}: The output is equivalent to fmt.Sprintf("%v", anything). Sample Code First, write the template: {% func Types(a int, b float64, c []byte, d string) %} int: {%d a %}, float64: {%f.2 b %}, bytes: {%z c %}, string with quotes: {%q d %}, string without quotes: {%j d %}. {% endfunc %} Then use it: func main() { fmt.Println(templates.Types(1, 5.75, []byte{'a', 'b', 'c'}, "hello")) } Run it: $ go run . Output result: int: 1, float64: 5.75,

Apr 9, 2025 - 13:08
 0
Jinja2 for Go? Meet Quicktemplate — and Ditch html/template for Good

Image description

Leapcell: The Best of Serverless Web Hosting

Quicktemplate Template Library Usage Guide

Recently, when organizing the code of our project, I found that the code for many activities was very similar in structure and the functions it provided. To facilitate future development, I spent some time writing a tool for generating code frameworks to minimize repetitive work. The code itself is not complex and is closely related to the project code, so I won't go into details here. During this process, I found that the Go standard template libraries text/template and html/template were rather restrictive and inconvenient to use. I learned from GitHub about the third-party template library quicktemplate, which is powerful, has a simple syntax, and is easy to use. Today, we're going to introduce quicktemplate.

Quick Start

The code in this article uses Go Modules.

Initialize the Project

First, create the code directory and initialize it:

$ mkdir quicktemplate && cd quicktemplate
$ go mod init github.com/leapcell/quicktemplate

Install Dependencies

quicktemplate will convert the template code we write into Go language code. Therefore, we need to install the quicktemplate package and a compiler named qtc:

$ go get -u github.com/valyala/quicktemplate
$ go get -u github.com/valyala/quicktemplate/qtc

Write the Template File

First, we need to write a template file in the quicktemplate format. The template file by default has the .qtpl extension. Here, I've written a simple template file greeting.qtpl:

All text outside function is treated as comments.

{% func Greeting(name string, count int) %}
  {% for i := 0; i < count; i++ %}
    Hello, {%s name %}
  {% endfor %}
{% endfunc %}

Template Syntax Explanation

The template syntax is very simple. We just need to briefly understand the following two points:

  • Templates are in units of functions. Functions can accept parameters of any type and quantity, and these parameters can be used within the function. All text outside the function is a comment, and the qtc compiler will ignore the comments.
  • The content within the function, except for the syntax structure, will be output as-is in the rendered text, including spaces and line breaks.

Generate and Use the Template

Save greeting.qtpl to the templates directory, and then execute the qtc command. This command will generate the corresponding Go file greeting.qtpl.go, with the package name templates. Now, we can use this template:

package main

import (
  "fmt"

  "github.com/leapcell/quicktemplate/get-started/templates"
)

func main() {
  fmt.Println(templates.Greeting("leapcell", 5))
}

Run the Code

Call the template function, pass in the parameters, and return the rendered text:

$ go run .

Output result:

Hello, leapcell
Hello, leapcell
Hello, leapcell
Hello, leapcell
Hello, leapcell

{%s name %} performs text replacement, and {% for %} loops to generate repeated text. Multiple spaces and line breaks appear in the output because, except for the syntax structure within the function, other content will be retained as-is, including spaces and line breaks.

Notes

Since quicktemplate converts templates into Go code for use, if the template is modified, the qtc command must be executed first to regenerate the Go code; otherwise, the modifications will not take effect.

Syntax Structure

quicktemplate supports common Go syntax structures, such as if, for, func, import, return. And the way of writing is not much different from writing Go code directly, with almost no learning cost. However, when using these syntaxes in templates, they need to be enclosed in {% and %}, and if, for, etc. need to add endif, endfor to clearly indicate the end.

Variables

Above, we've already seen how to render the passed-in parameter name using {%s name %}. Since name is of the string type, s is used after {% to specify the type. quicktemplate also supports other types of values:

  • Integer: {%d int %}, {%dl int64 %}, {%dul uint64 %}.
  • Floating point number: {%f float %}. You can also set the output precision, using {%f.precision float %}. For example, {%f.2 1.2345 %} outputs 1.23.
  • Byte slice ([]byte): {%z bytes %}.
  • String: {%q str %} or byte slice: {%qz bytes %}. Quotes are escaped as ".
  • String: {%j str %} or byte slice: {%jz bytes %}. There are no quotes.
  • URL encoding: {%u str %}, {%uz bytes %}.
  • {%v anything %}: The output is equivalent to fmt.Sprintf("%v", anything).

Sample Code

First, write the template:

{% func Types(a int, b float64, c []byte, d string) %}
  int: {%d a %}, float64: {%f.2 b %}, bytes: {%z c %}, string with quotes: {%q d %}, string without quotes: {%j d %}.
{% endfunc %}

Then use it:

func main() {
  fmt.Println(templates.Types(1, 5.75, []byte{'a', 'b', 'c'}, "hello"))
}

Run it:

$ go run .

Output result:

int: 1, float64: 5.75, bytes: abc, string with quotes: "hello", string without quotes: hello.

Calling Functions

quicktemplate supports calling template functions and functions from the standard library within templates. Since qtc will directly generate Go code, we can even write our own functions in the same directory for the templates to call. Functions defined in template A can also call functions defined in template B.

Define Custom Functions

First, we write a file rank.go in the templates directory and define a Rank function that takes in a score and returns a rating:

package templates

func Rank(score int) string {
  if score >= 90 {
    return "A"
  } else if score >= 80 {
    return "B"
  } else if score >= 70 {
    return "C"
  } else if score >= 60 {
    return "D"
  } else {
    return "E"
  }
}

Call Custom Functions in Templates

Then we can call this function in the template:

{% import "fmt" %}
{% func ScoreList(name2score map[string]int) %}
  {% for name, score := range name2score %}
    {%s fmt.Sprintf("%s: score-%d rank-%s", name, score, Rank(score)) %}
  {% endfor %}
{% endfunc %}

Compile and Use the Template

Compile the template:

$ qtc

Write the program:

func main() {
  name2score := make(map[string]int)
  name2score["leapcell"] = 85
  name2score["bob"] = 86
  name2score["alice"] = 22

  fmt.Println(templates.ScoreList(name2score))
}

Run the program and output:

$ go run .

Output result:

leapcell: score-85 rank-B
bob: score-86 rank-A
alice: score-22 rank-E

Since we use the fmt package in the template, we need to import the package first using {% import %}. Calling functions of another template in a template is similar because templates will eventually be converted into Go code, and there are functions with the same signature in the Go code.

Web Applications

quicktemplate is often used to write templates for HTML pages:

{% func Index(name string) %}

  
    Awesome Web
  
  
    

Hi, {%s name %}

Welcome to the awesome web!!! {% endfunc %}

Next, write a simple web server:

func index(w http.ResponseWriter, r *http.Request) {
  templates.WriteIndex(w, r.FormValue("name"))
}

func main() {
  mux := http.NewServeMux()
  mux.HandleFunc("/", index)

  server := &http.Server{
    Handler: mux,
    Addr:    ":8080",
  }

  log.Fatal(server.ListenAndServe())
}

qtc will generate a Write* method, which accepts a parameter of type io.Writer. The result of rendering the template is written into this io.Writer. We can directly pass http.ResponseWriter as a parameter, which is very convenient.

Run the Web Server

$ qtc
$ go run .

Enter localhost:8080?name=leapcell in the browser to view the result.

Conclusion

quicktemplate has at least the following three advantages:

  • The syntax is very similar to that of the Go language, with almost no learning cost.
  • It will be converted into Go first, and the rendering speed is very fast, more than 20 times faster than the standard library html/template.
  • For security reasons, it will perform some encoding to avoid being attacked.

Leapcell: The Best of Serverless Web Hosting

Finally, I recommend to you a platform that is most suitable for deploying Go services: Leapcell

Image description