Echo Template Layouts
Introduction
When building web applications with Echo, organizing your HTML structure becomes crucial as your project grows. Echo Template Layouts provide a powerful way to create consistent page structures across your application while avoiding code duplication. Layouts act as "wrapper templates" that contain common elements like headers, footers, navigation bars, and basic HTML structure, allowing individual pages to focus solely on their unique content.
In this guide, we'll explore how to create, customize, and use layouts in Echo Templates, making your web development process more efficient and maintainable.
Understanding Template Layouts
What is a Layout?
A layout is a template that defines the common structure of multiple pages in your web application. Think of it as the frame or container that holds your content. For example, most websites have consistent headers, footers, and navigation bars across all pages - these common elements are perfect candidates for inclusion in a layout template.
Benefits of Using Layouts
- Code Reusability: Define common elements once and reuse them across multiple pages
- Consistency: Maintain a consistent look and feel throughout your application
- Maintainability: Update common elements in one place rather than across multiple files
- Separation of Concerns: Focus page templates on their specific content, not the surrounding structure
Creating Your First Layout
Let's start by creating a basic layout template. In Echo applications with templates, layouts are typically stored in a layouts
or templates/layouts
directory.
Basic Layout Structure
<!-- templates/layouts/main.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{template "title" .}} - My Echo App</title>
<link rel="stylesheet" href="/static/css/main.css">
{{block "css" .}}{{end}}
</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>© {{currentYear}} My Echo Application</p>
</footer>
<script src="/static/js/main.js"></script>
{{block "js" .}}{{end}}
</body>
</html>
Let's break down what's happening in this layout:
{{template "title" .}}
defines a placeholder for page-specific titles{{block "css" .}}{{end}}
allows pages to include additional CSS if needed{{template "content" .}}
defines where the main content of each page will be inserted{{block "js" .}}{{end}}
allows pages to include additional JavaScript if needed
Using Layouts in Page Templates
Now that we have our layout, let's create a page template that uses it:
<!-- templates/home.html -->
{{define "title"}}Home{{end}}
{{define "content"}}
<div class="welcome">
<h1>Welcome to My Echo Application</h1>
<p>This is the home page of our Echo application.</p>
<div class="features">
<div class="feature">
<h3>Fast</h3>
<p>Built with performance in mind</p>
</div>
<div class="feature">
<h3>Secure</h3>
<p>Security best practices baked in</p>
</div>
<div class="feature">
<h3>Scalable</h3>
<p>Designed to grow with your needs</p>
</div>
</div>
</div>
{{end}}
{{define "css"}}
<style>
.welcome { text-align: center; padding: 2rem; }
.features { display: flex; justify-content: space-around; margin-top: 2rem; }
.feature { padding: 1rem; border: 1px solid #eee; border-radius: 5px; }
</style>
{{end}}
This page template defines three blocks that will be inserted into our layout:
title
- The page title that appears in the browser tabcontent
- The main content unique to this pagecss
- Page-specific CSS styles
Rendering Templates with Layouts in Echo
To use these templates in your Echo application, you'll need to configure the template renderer and set up routes that render these templates.
package main
import (
"html/template"
"io"
"time"
"github.com/labstack/echo/v4"
)
// Template renderer
type Template struct {
templates *template.Template
}
func (t *Template) Render(w io.Writer, name string, data interface{}, c echo.Context) error {
return t.templates.ExecuteTemplate(w, name, data)
}
func currentYear() int {
return time.Now().Year()
}
func main() {
e := echo.New()
// Static files
e.Static("/static", "static")
// Set up template renderer
funcMap := template.FuncMap{
"currentYear": currentYear,
}
t := &Template{
templates: template.Must(template.New("").
Funcs(funcMap).
ParseGlob("templates/**/*.html")),
}
e.Renderer = t
// Routes
e.GET("/", func(c echo.Context) error {
return c.Render(200, "layouts/main.html", map[string]interface{}{
"name": "Echo User",
})
})
e.Logger.Fatal(e.Start(":8080"))
}
In this example:
- We create a custom template renderer that supports our layout structure
- We register a
currentYear
function to use in our templates - We configure Echo to use our renderer
- We set up a route that renders the home page using our main layout
Nested Layouts
As your application grows, you might need more complex layout structures. Echo Templates support nested layouts, allowing you to create specialized layouts for different sections of your site.
Creating a Nested Layout
<!-- templates/layouts/admin.html -->
{{define "title"}}{{template "admin-title" .}} - Admin Panel{{end}}
{{define "content"}}
<div class="admin-container">
<aside class="admin-sidebar">
<h3>Admin Menu</h3>
<ul>
<li><a href="/admin/dashboard">Dashboard</a></li>
<li><a href="/admin/users">Users</a></li>
<li><a href="/admin/settings">Settings</a></li>
</ul>
</aside>
<div class="admin-content">
{{template "admin-content" .}}
</div>
</div>
{{end}}
{{define "css"}}
<link rel="stylesheet" href="/static/css/admin.css">
{{template "admin-css" .}}
{{end}}
{{define "js"}}
<script src="/static/js/admin.js"></script>
{{template "admin-js" .}}
{{end}}
And then use this admin layout in an admin page:
<!-- templates/admin/dashboard.html -->
{{define "admin-title"}}Dashboard{{end}}
{{define "admin-content"}}
<h1>Admin Dashboard</h1>
<div class="dashboard-stats">
<div class="stat-card">
<h3>Users</h3>
<p class="number">{{.userCount}}</p>
</div>
<div class="stat-card">
<h3>Posts</h3>
<p class="number">{{.postCount}}</p>
</div>
<div class="stat-card">
<h3>Comments</h3>
<p class="number">{{.commentCount}}</p>
</div>
</div>
{{end}}
{{define "admin-css"}}
<style>
.dashboard-stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; }
.stat-card { background: #f8f9fa; padding: 1rem; border-radius: 5px; text-align: center; }
.number { font-size: 2rem; font-weight: bold; color: #3498db; }
</style>
{{end}}
{{define "admin-js"}}
<script>
console.log('Dashboard script loaded');
</script>
{{end}}
To render this nested structure, you'd need to adapt your Echo handler:
e.GET("/admin/dashboard", func(c echo.Context) error {
data := map[string]interface{}{
"userCount": 1250,
"postCount": 342,
"commentCount": 8761,
}
return c.Render(200, "layouts/main.html", data)
})
Dynamic Layout Selection
Sometimes you need to choose different layouts based on certain conditions. For example, showing a simplified layout for mobile users or a different layout for authenticated vs. anonymous users:
e.GET("/profile", func(c echo.Context) error {
user := getCurrentUser(c)
data := map[string]interface{}{
"user": user,
}
// Choose layout based on user status
layoutTemplate := "layouts/main.html"
if user.IsPremium {
layoutTemplate = "layouts/premium.html"
}
return c.Render(200, layoutTemplate, data)
})
Practical Application: Multi-theme Support
Using layouts, you can implement theme support in your Echo application. Here's a simplified approach:
<!-- templates/layouts/theme_light.html -->
<!DOCTYPE html>
<html lang="en" data-theme="light">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{template "title" .}} - My Echo App</title>
<link rel="stylesheet" href="/static/css/themes/light.css">
{{block "css" .}}{{end}}
</head>
<body>
<!-- Layout content similar to main.html -->
<main>{{template "content" .}}</main>
</body>
</html>
<!-- templates/layouts/theme_dark.html has similar structure but with dark theme -->
In your Echo application:
func getThemeLayout(c echo.Context) string {
// Check user preference from cookie
cookie, err := c.Cookie("theme")
if err == nil && cookie.Value == "dark" {
return "layouts/theme_dark.html"
}
// Default to light theme
return "layouts/theme_light.html"
}
e.GET("/", func(c echo.Context) error {
layout := getThemeLayout(c)
return c.Render(200, layout, map[string]interface{}{
"name": "Echo User",
})
})
This allows users to switch between themes, with the appropriate layout being selected based on their preference.
Summary
Echo Template Layouts provide a powerful way to structure your web applications, promoting code reuse and maintaining consistency across your website. By separating the common structure from page-specific content, you make your codebase more maintainable and easier to update.
In this guide, we've covered:
- Creating basic layout templates
- Using layouts in page templates
- Configuring Echo to render templates with layouts
- Implementing nested layouts for more complex structures
- Dynamically selecting layouts based on conditions
- Practical application with multi-theme support
Additional Resources
Exercises
- Create a layout with a sidebar that shows different navigation links based on whether the user is logged in
- Implement a mobile-responsive layout that changes structure on smaller screens
- Create a specialized layout for a blog section of your website with recent posts in the sidebar
- Add dark mode support to your layouts with a toggle button that persists the user's preference
- Implement breadcrumb navigation in your layouts that dynamically updates based on the current page
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)