Skip to main content

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

go
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:

html
<!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)

go
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

go
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:

html
<!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

go
// 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:

go
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:

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>&copy; 2023 My Gin Application</p>
</footer>
</body>
</html>

Create a page using the layout

Create a file named templates/pages/about.html:

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

go
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:

go
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

go
type Author struct {
Name string
Email string
}

type BlogPost struct {
Title string
Content string
Author Author
Tags []string
PostDate time.Time
}

Route Handler

go
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:

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:

go
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:

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:

html
<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:

html
<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

  1. Create a simple portfolio website with multiple pages (Home, Projects, Contact) using template inheritance.
  2. Build a product listing page that displays products from a slice of structs.
  3. Implement a navigation menu that highlights the current page using conditional rendering.
  4. Create a custom template function that formats currency values with the appropriate symbol.
  5. 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! :)