Skip to main content

Gin Template Partials

When building web applications with Go's Gin framework, you'll often find yourself needing to reuse certain components across multiple pages. This is where template partials come in handy. Partials allow you to split your templates into smaller, reusable components, making your code more modular, maintainable, and DRY (Don't Repeat Yourself).

What Are Template Partials?

Template partials are fragments of HTML/template code that can be included in other templates. They're particularly useful for elements that appear across multiple pages such as:

  • Headers and navigation bars
  • Footers
  • Sidebars
  • Common UI components like alert boxes or form elements

By using partials, you can define these elements once and reuse them throughout your application.

Setting Up Template Partials in Gin

Gin uses Go's standard html/template package under the hood, which provides the ability to include partials through various mechanisms. Let's explore how to implement them:

Basic Project Structure

First, let's set up a typical directory structure for templates:

project/
├── main.go
├── templates/
│ ├── layouts/
│ │ └── base.html
│ ├── partials/
│ │ ├── header.html
│ │ ├── footer.html
│ │ └── navbar.html
│ └── pages/
│ ├── home.html
│ └── about.html

Loading Templates in Gin

To load templates in Gin, including partials, we use the LoadHTMLGlob or LoadHTMLFiles methods:

go
func main() {
r := gin.Default()

// Load all templates, including partials
r.LoadHTMLGlob("templates/**/*")

// Define routes
r.GET("/", func(c *gin.Context) {
c.HTML(http.StatusOK, "pages/home.html", gin.H{
"title": "Home Page",
})
})

r.Run(":8080")
}

Creating and Using Partials

1. Define Partial Templates

Let's create a simple header partial:

html
<!-- templates/partials/header.html -->
{{ define "header" }}
<header class="site-header">
<div class="logo">
<h1>My Website</h1>
</div>
{{ template "navbar" . }}
</header>
{{ end }}

And a navbar partial:

html
<!-- templates/partials/navbar.html -->
{{ define "navbar" }}
<nav class="main-nav">
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
</nav>
{{ end }}

And a footer partial:

html
<!-- templates/partials/footer.html -->
{{ define "footer" }}
<footer class="site-footer">
<p>&copy; {{ .Year }} My Website. All rights reserved.</p>
</footer>
{{ end }}

2. Create a Base Layout Template

The base layout template serves as the foundation for all pages:

html
<!-- templates/layouts/base.html -->
{{ define "layouts/base" }}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ .title }}</title>
<link rel="stylesheet" href="/static/css/styles.css">
</head>
<body>
{{ template "header" . }}

<main>
{{ block "content" . }}{{ end }}
</main>

{{ template "footer" . }}

<script src="/static/js/main.js"></script>
</body>
</html>
{{ end }}

3. Create Page Templates That Use the Base and Partials

html
<!-- templates/pages/home.html -->
{{ define "pages/home.html" }}
{{ template "layouts/base" . }}
{{ end }}

{{ define "content" }}
<section class="home-hero">
<h1>Welcome to My Website</h1>
<p>This is the home page of my awesome Gin application.</p>
</section>
<section class="features">
<h2>Features</h2>
<ul>
<li>Fast and lightweight</li>
<li>Easy to use</li>
<li>Highly customizable</li>
</ul>
</section>
{{ end }}

4. Update Your Routes to Pass Necessary Data

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

r.GET("/", func(c *gin.Context) {
c.HTML(http.StatusOK, "pages/home.html", gin.H{
"title": "Home Page",
"Year": time.Now().Year(),
})
})

r.GET("/about", func(c *gin.Context) {
c.HTML(http.StatusOK, "pages/about.html", gin.H{
"title": "About Us",
"Year": time.Now().Year(),
})
})

r.Run(":8080")
}

Advanced Partial Techniques

Conditional Partials

Sometimes you may want to include different partials based on certain conditions:

html
{{ define "content" }}
<h1>{{ .title }}</h1>

{{ if .user.IsAdmin }}
{{ template "admin_panel" . }}
{{ else }}
{{ template "user_panel" . }}
{{ end }}
{{ end }}

Passing Specific Data to Partials

You can pass specific data to partials rather than passing the entire data context:

html
{{ define "content" }}
<h1>{{ .title }}</h1>

<!-- Pass only the user data to the user_details partial -->
{{ template "user_details" .user }}

<!-- Continue with the rest of your content -->
<div class="main-content">
{{ .content }}
</div>
{{ end }}

Dynamic Partials

You can also dynamically choose which partial to use:

go
r.GET("/dashboard", func(c *gin.Context) {
userRole := getUserRole(c)

// Choose the appropriate dashboard template based on user role
dashboardTemplate := "partials/user_dashboard"
if userRole == "admin" {
dashboardTemplate = "partials/admin_dashboard"
}

c.HTML(http.StatusOK, "pages/dashboard.html", gin.H{
"title": "Dashboard",
"dashboardPartial": dashboardTemplate,
})
})

Then in your template:

html
{{ define "content" }}
<h1>{{ .title }}</h1>

{{ template .dashboardPartial . }}
{{ end }}

Real-world Example: A Blog with Partials

Let's put everything together with a blog example:

Step 1: Define Common Partials

html
<!-- templates/partials/post_summary.html -->
{{ define "post_summary" }}
<article class="post-summary">
<h3><a href="/post/{{ .ID }}">{{ .Title }}</a></h3>
<div class="post-meta">
<span class="date">{{ .FormattedDate }}</span>
<span class="author">By {{ .Author }}</span>
</div>
<p class="excerpt">{{ .Excerpt }}</p>
<a href="/post/{{ .ID }}" class="read-more">Read more...</a>
</article>
{{ end }}

Step 2: Create Page Templates

html
<!-- templates/pages/blog.html -->
{{ define "pages/blog.html" }}
{{ template "layouts/base" . }}
{{ end }}

{{ define "content" }}
<section class="blog-list">
<h1>Blog Posts</h1>

{{ range .posts }}
{{ template "post_summary" . }}
{{ else }}
<p class="no-posts">No blog posts found.</p>
{{ end }}

{{ template "pagination" .pagination }}
</section>
{{ end }}

Step 3: In Your Go Code

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

r.GET("/blog", func(c *gin.Context) {
posts := fetchBlogPosts()

// Format the data for the template
formattedPosts := make([]gin.H, len(posts))
for i, post := range posts {
formattedPosts[i] = gin.H{
"ID": post.ID,
"Title": post.Title,
"FormattedDate": post.Date.Format("January 2, 2006"),
"Author": post.Author,
"Excerpt": truncateText(post.Content, 150),
}
}

c.HTML(http.StatusOK, "pages/blog.html", gin.H{
"title": "Blog",
"Year": time.Now().Year(),
"posts": formattedPosts,
"pagination": gin.H{
"currentPage": 1,
"totalPages": 5,
},
})
})

r.Run(":8080")
}

func truncateText(text string, length int) string {
if len(text) <= length {
return text
}
return text[:length] + "..."
}

Organizing Partials by Feature

For larger applications, you might want to organize partials by feature rather than just putting them all in one folder:

templates/
├── layouts/
│ └── base.html
├── partials/
│ ├── common/
│ │ ├── header.html
│ │ └── footer.html
│ ├── blog/
│ │ ├── post_summary.html
│ │ ├── post_full.html
│ │ └── comment.html
│ └── user/
│ ├── profile.html
│ └── settings_form.html
└── pages/
├── home.html
├── blog.html
└── user_profile.html

This organization makes it easier to maintain and understand the structure as your application grows.

Summary

Template partials in Gin are a powerful way to make your web application more maintainable and DRY. By breaking down your templates into smaller, reusable components, you can:

  • Reduce code duplication
  • Improve code organization
  • Make template changes more manageable
  • Create consistency across your web application

Remember that partials in Gin are built on Go's standard template system, which provides flexible ways to include and compose templates. Understanding these mechanisms will help you create maintainable and well-structured web applications.

Additional Resources

Exercises

  1. Create a website with a header, footer, and sidebar as partials
  2. Implement a dynamic navigation menu that highlights the current page
  3. Build a reusable form component that can be used for both creating and editing resources
  4. Create a partial that displays user notifications, with different styles based on the notification type (success, error, info, warning)
  5. Build a blog template system with partials for post listing, individual post view, and comments


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