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,

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 %}
outputs1.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 tofmt.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