Skip to main content

Gin Template Basics

Introduction

When building web applications with Go's Gin framework, you'll often need to generate dynamic HTML pages that display data from your server. This is where Gin's template functionality comes in. Templates allow you to define HTML layouts with special placeholders that Gin will replace with actual data when rendering the page.

Gin leverages Go's built-in html/template package but adds convenient helpers to make template rendering more straightforward. In this guide, we'll explore the basics of using templates in Gin applications, from setting up template rendering to displaying dynamic data.

Setting Up Template Rendering in Gin

Before you can use templates in your Gin application, you need to load and configure them. Let's start with the most basic setup:

go
package main

import (
"github.com/gin-gonic/gin"
"net/http"
)

func main() {
router := gin.Default()

// Load HTML templates from the "templates" folder
router.LoadHTMLGlob("templates/*")

// Define a route that will render a template
router.GET("/", func(c *gin.Context) {
c.HTML(http.StatusOK, "index.html", gin.H{
"title": "Gin Template Example",
})
})

router.Run(":8080")
}

In this example, LoadHTMLGlob tells Gin to load all HTML templates from the "templates" directory. The c.HTML() function renders the template, passing in:

  1. The HTTP status code
  2. The name of the template file
  3. A map or struct containing data to pass to the template

Creating Your First Template

Let's create a simple template file. Create a directory called templates in your project root and add a file named index.html:

html
<!DOCTYPE html>
<html>
<head>
<title>{{ .title }}</title>
</head>
<body>
<h1>Welcome to {{ .title }}</h1>
<p>This is a simple template example.</p>
</body>
</html>

When rendered, the {{ .title }} placeholders will be replaced with the value of the title field from the data we passed in the route handler.

Passing Data to Templates

You can pass various types of data to your templates:

go
router.GET("/user", func(c *gin.Context) {
c.HTML(http.StatusOK, "user.html", gin.H{
"title": "User Profile",
"name": "John Doe",
"age": 30,
"active": true,
"skills": []string{"Go", "JavaScript", "Python"},
})
})

Then in your templates/user.html file:

html
<!DOCTYPE html>
<html>
<head>
<title>{{ .title }}</title>
</head>
<body>
<h1>{{ .title }}</h1>

<h2>User Details</h2>
<p>Name: {{ .name }}</p>
<p>Age: {{ .age }}</p>

<p>Account status:
{{ if .active }}
<span style="color: green">Active</span>
{{ else }}
<span style="color: red">Inactive</span>
{{ end }}
</p>

<h3>Skills:</h3>
<ul>
{{ range .skills }}
<li>{{ . }}</li>
{{ end }}
</ul>
</body>
</html>

This example demonstrates:

  • Accessing simple string, integer, and boolean values
  • Using conditionals with if statements
  • Looping through slices with range

Template Syntax Basics

Here are some common template directives you'll use in Gin templates:

Variable Output

To output a variable's value, use double curly braces:

{{ .variableName }}

The dot (.) refers to the current data passed to the template.

Conditionals

{{ if .condition }}
<!-- Content displayed when condition is true -->
{{ else }}
<!-- Content displayed when condition is false -->
{{ end }}

Loops with Range

{{ range .items }}
<!-- .items is the slice/array/map being iterated -->
<!-- Inside the range, . refers to the current item -->
{{ . }}
{{ else }}
<!-- Content displayed when items is empty -->
{{ end }}

Comments

You can add comments that won't be rendered in the final HTML:

{{/* This is a comment and won't be rendered */}}

Template Functions

Go templates include many built-in functions to manipulate data:

html
<p>{{ .text | lower }}</p> <!-- Convert to lowercase -->
<p>{{ len .items }}</p> <!-- Get length of an array/slice/string -->
<p>{{ if eq .name "John" }}Hello John!{{ end }}</p> <!-- Equality check -->

Some common built-in functions:

  • eq: Equal
  • ne: Not equal
  • lt: Less than
  • gt: Greater than
  • and: Logical AND
  • or: Logical OR
  • not: Logical NOT
  • len: Length of array, slice, or string
  • index: Access map or array element

Template Composition

Gin supports template composition using the template action:

html
<!-- base.html -->
<!DOCTYPE html>
<html>
<head>
<title>{{ .title }}</title>
<link rel="stylesheet" href="/static/css/main.css">
</head>
<body>
<header>
<h1>My Website</h1>
</header>

<main>
{{ template "content" . }}
</main>

<footer>
<p>&copy; 2023 My Website</p>
</footer>
</body>
</html>
html
<!-- page.html -->
{{ define "content" }}
<div class="container">
<h2>{{ .pageTitle }}</h2>
<p>{{ .pageContent }}</p>
</div>
{{ end }}

To use nested templates, update your Gin setup:

go
// Load all templates
router.LoadHTMLGlob("templates/**/*")

router.GET("/page", func(c *gin.Context) {
c.HTML(http.StatusOK, "base.html", gin.H{
"title": "My Page",
"pageTitle": "Welcome to My Page",
"pageContent": "This is the page content.",
})
})

A Real-World Example: Product List

Let's create a more practical example that displays a list of products:

go
package main

import (
"github.com/gin-gonic/gin"
"net/http"
)

// Product represents a product in our store
type Product struct {
ID int
Name string
Price float64
InStock bool
Category string
}

func main() {
router := gin.Default()
router.LoadHTMLGlob("templates/*")

// Serve static files like CSS
router.Static("/static", "./static")

router.GET("/products", func(c *gin.Context) {
products := []Product{
{ID: 1, Name: "Laptop", Price: 999.99, InStock: true, Category: "Electronics"},
{ID: 2, Name: "Headphones", Price: 99.99, InStock: true, Category: "Electronics"},
{ID: 3, Name: "Keyboard", Price: 49.99, InStock: false, Category: "Electronics"},
{ID: 4, Name: "Coffee Mug", Price: 14.99, InStock: true, Category: "Kitchen"},
}

c.HTML(http.StatusOK, "products.html", gin.H{
"title": "Product Catalog",
"products": products,
})
})

router.Run(":8080")
}

And the template:

html
<!-- templates/products.html -->
<!DOCTYPE html>
<html>
<head>
<title>{{ .title }}</title>
<style>
table { border-collapse: collapse; width: 100%; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
tr:nth-child(even) { background-color: #f2f2f2; }
th { background-color: #4CAF50; color: white; }
.out-of-stock { color: red; }
.in-stock { color: green; }
</style>
</head>
<body>
<h1>{{ .title }}</h1>

<table>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Price</th>
<th>Status</th>
<th>Category</th>
</tr>
</thead>
<tbody>
{{ range .products }}
<tr>
<td>{{ .ID }}</td>
<td>{{ .Name }}</td>
<td>${{ printf "%.2f" .Price }}</td>
<td>
{{ if .InStock }}
<span class="in-stock">In Stock</span>
{{ else }}
<span class="out-of-stock">Out of Stock</span>
{{ end }}
</td>
<td>{{ .Category }}</td>
</tr>
{{ else }}
<tr>
<td colspan="5">No products found</td>
</tr>
{{ end }}
</tbody>
</table>
</body>
</html>

This example shows:

  • A more complex data structure (slice of structs)
  • Formatting output with the printf function
  • Conditional styling based on data values
  • Handling empty collections with the else clause in range

Summary

In this guide, we've covered the basics of using templates with the Gin framework:

  1. Setting up template rendering with LoadHTMLGlob
  2. Creating templates with placeholders for dynamic data
  3. Passing data to templates using c.HTML()
  4. Using template directives like conditionals and loops
  5. Working with template functions and composition
  6. Building a real-world product list example

Templates are a powerful way to generate dynamic HTML in your Gin applications. They separate presentation from business logic, making your code more maintainable and easier to understand.

Additional Resources and Exercises

Resources

Exercises

  1. Blog Post Template: Create a template for displaying blog posts with title, author, date, content, and tags.

  2. Form Template: Build a template for a user registration form that displays validation errors next to each field.

  3. Admin Dashboard: Design a dashboard template that shows different content based on the user's role (admin vs. regular user).

  4. Template Inheritance: Create a base layout template and multiple page templates that extend it, each with different content sections.

  5. Dynamic Navigation: Build a template with a navigation bar that highlights the current page and displays different menu items based on whether the user is logged in.

Remember that practice is key to mastering templates. The more you work with them, the more comfortable you'll become with their syntax and capabilities!



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)