Gin Template Rendering
When building web applications, serving dynamic HTML content is a common requirement. The Gin framework provides an elegant way to render HTML templates with data from your Go backend. This guide will walk you through the process of template rendering in Gin from basic setup to advanced techniques.
Introduction to Gin Template Rendering
Gin uses the standard Go html/template
package under the hood, which provides powerful templating capabilities with logic, loops, conditionals, and more. Template rendering allows you to:
- Create dynamic HTML pages with Go data
- Separate business logic from presentation
- Reuse common HTML components across multiple pages
- Implement layouts and partials for consistent design
Setting Up Templates in Gin
Before rendering templates, you need to set up the template engine and specify where your template files are located.
Basic Template Setup
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
r := gin.Default()
// Load HTML templates from the "templates" folder
r.LoadHTMLGlob("templates/*")
// Define a route that renders a template
r.GET("/", func(c *gin.Context) {
c.HTML(http.StatusOK, "index.html", gin.H{
"title": "Gin Template Example",
})
})
r.Run(":8080")
}
In this example, we're telling Gin to look for HTML templates in the templates
directory with LoadHTMLGlob("templates/*")
.
Creating Your First Template
Let's create a simple HTML template to render. Create a file named index.html
in your templates
directory:
<!DOCTYPE html>
<html>
<head>
<title>{{ .title }}</title>
</head>
<body>
<h1>{{ .title }}</h1>
<p>Welcome to Gin template rendering!</p>
</body>
</html>
When you run the application and visit localhost:8080
, you'll see a page with the title "Gin Template Example" and the welcome message.
Passing Data to Templates
You can pass various types of data to your templates using the gin.H
map or any Go struct.
Using gin.H (Map)
r.GET("/user", func(c *gin.Context) {
c.HTML(http.StatusOK, "user.html", gin.H{
"title": "User Profile",
"name": "John Doe",
"age": 30,
"roles": []string{"Admin", "Editor"},
})
})
Using a Struct
type User struct {
Name string
Age int
Roles []string
}
r.GET("/user-struct", func(c *gin.Context) {
user := User{
Name: "Jane Smith",
Age: 28,
Roles: []string{"Developer", "Tester"},
}
c.HTML(http.StatusOK, "user.html", gin.H{
"title": "User Profile",
"user": user,
})
})
And the corresponding user.html
template:
<!DOCTYPE html>
<html>
<head>
<title>{{ .title }}</title>
</head>
<body>
<h1>{{ .title }}</h1>
<!-- If using gin.H map directly -->
<div>
<p>Name: {{ .name }}</p>
<p>Age: {{ .age }}</p>
<p>Roles:</p>
<ul>
{{range .roles}}
<li>{{.}}</li>
{{end}}
</ul>
</div>
<!-- If using a struct -->
{{if .user}}
<div>
<p>Name: {{ .user.Name }}</p>
<p>Age: {{ .user.Age }}</p>
<p>Roles:</p>
<ul>
{{range .user.Roles}}
<li>{{.}}</li>
{{end}}
</ul>
</div>
{{end}}
</body>
</html>
Template Organization with Multiple Files
For larger applications, you'll want to organize your templates into multiple files and folders.
Using Nested Directories
// Load templates from multiple directories
r.LoadHTMLGlob("templates/**/*")
This will recursively load templates from all subdirectories of the templates
folder.
Template Naming with Directories
When using nested directories, template names include their path:
r.GET("/admin/dashboard", func(c *gin.Context) {
c.HTML(http.StatusOK, "admin/dashboard.html", gin.H{
"title": "Admin Dashboard",
})
})
Template Inheritance and Layouts
You can implement layout patterns in your templates for reusable page structures.
Create a base layout template
Create a file named templates/layouts/base.html
:
<!DOCTYPE html>
<html>
<head>
<title>{{ .title }}</title>
<link rel="stylesheet" href="/assets/css/main.css">
</head>
<body>
<header>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
<a href="/contact">Contact</a>
</nav>
</header>
<main>
{{ template "content" . }}
</main>
<footer>
<p>© 2023 My Gin Application</p>
</footer>
</body>
</html>
Create a page using the layout
Create a file named templates/pages/about.html
:
{{ define "content" }}
<div class="about-page">
<h1>About Us</h1>
<p>This is a demonstration of template inheritance in Gin.</p>
<p>Company founded: {{ .founded }}</p>
</div>
{{ end }}
Rendering the page with the layout
r.GET("/about", func(c *gin.Context) {
c.HTML(http.StatusOK, "pages/about.html", gin.H{
"title": "About Us",
"founded": 2020,
})
})
To make this work, you need to configure Gin to load both files:
r.LoadHTMLGlob("templates/**/*")
Practical Example: Dynamic Blog Post Rendering
Let's build a more complete example showing a blog post page with dynamic content.
Data Models
type Author struct {
Name string
Email string
}
type BlogPost struct {
Title string
Content string
Author Author
Tags []string
PostDate time.Time
}
Route Handler
r.GET("/blog/:id", func(c *gin.Context) {
// In a real app, you would fetch this from a database
post := BlogPost{
Title: "Getting Started with Gin Templates",
Content: "Gin is a high-performance web framework written in Go...",
Author: Author{
Name: "Go Developer",
Email: "[email protected]",
},
Tags: []string{"go", "gin", "templates", "web-development"},
PostDate: time.Date(2023, 5, 15, 14, 30, 0, 0, time.UTC),
}
c.HTML(http.StatusOK, "blog/post.html", gin.H{
"title": post.Title,
"post": post,
})
})
Blog Post Template
Create a file named templates/blog/post.html
:
{{ define "content" }}
<article class="blog-post">
<h1>{{ .post.Title }}</h1>
<div class="metadata">
<span class="date">{{ .post.PostDate.Format "January 2, 2006" }}</span>
<span class="author">by {{ .post.Author.Name }}</span>
</div>
<div class="tags">
{{range .post.Tags}}
<span class="tag">{{.}}</span>
{{end}}
</div>
<div class="content">
{{ .post.Content }}
</div>
<div class="author-bio">
<h3>About the author</h3>
<p>{{ .post.Author.Name }} ({{ .post.Author.Email }})</p>
</div>
</article>
{{ end }}
Advanced Template Features
Gin templates support all the features of Go's html/template
package. Here are some advanced techniques:
Custom Template Functions
You can create custom functions to use in your templates:
import (
"html/template"
"strings"
"time"
)
func main() {
r := gin.Default()
// Create a template with custom functions
html := template.Must(template.New("").Funcs(template.FuncMap{
"formatAsDate": func(t time.Time) string {
return t.Format("Jan 02, 2006")
},
"upper": strings.ToUpper,
}).ParseGlob("templates/**/*"))
r.SetHTMLTemplate(html)
r.GET("/custom", func(c *gin.Context) {
c.HTML(http.StatusOK, "custom.html", gin.H{
"now": time.Now(),
"text": "hello world",
})
})
r.Run(":8080")
}
In your templates/custom.html
:
<!DOCTYPE html>
<html>
<head>
<title>Custom Functions</title>
</head>
<body>
<p>Current date: {{ formatAsDate .now }}</p>
<p>Uppercase: {{ upper .text }}</p>
</body>
</html>
Conditional Rendering
You can use conditionals in your templates:
<div>
{{ if .user.IsAdmin }}
<a href="/admin">Admin Panel</a>
{{ else if .user.IsEditor }}
<a href="/editor">Editor Dashboard</a>
{{ else }}
<a href="/account">My Account</a>
{{ end }}
</div>
Loops and Ranges
You can iterate over slices, arrays, or maps:
<ul class="menu">
{{ range $index, $item := .menuItems }}
<li class="{{ if eq $index 0 }}active{{ end }}">
<a href="{{ $item.URL }}">{{ $item.Name }}</a>
</li>
{{ else }}
<li>No menu items available</li>
{{ end }}
</ul>
Common Pitfalls and Solutions
Template Not Found Errors
If you get "template not found" errors:
- Double-check the template name and path
- Ensure your
LoadHTMLGlob
pattern matches all needed files - Use
r.LoadHTMLFiles()
to explicitly load specific templates
Data Not Displaying in Template
If your template doesn't show the expected data:
- Check if the variable names in the template match your data keys
- Verify the data type and structure (use
fmt.Printf("%+v", yourData)
to debug) - Remember template variables are case-sensitive
Summary
In this guide, we've covered:
- Setting up template rendering in Gin
- Creating and organizing templates
- Passing data to templates using both
gin.H
and structs - Template inheritance with layouts
- Advanced features like custom functions and conditionals
- Common pitfalls and their solutions
Template rendering in Gin gives you a powerful way to generate dynamic HTML content for your web applications. By separating your business logic from your presentation, you create more maintainable and modular code.
Exercises
- Create a simple portfolio website with multiple pages (Home, Projects, Contact) using template inheritance.
- Build a product listing page that displays products from a slice of structs.
- Implement a navigation menu that highlights the current page using conditional rendering.
- Create a custom template function that formats currency values with the appropriate symbol.
- Build a pagination component that shows page numbers based on the total number of items.
Additional Resources
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)