Skip to main content

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

html
<!-- 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>&copy; {{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:

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

  1. title - The page title that appears in the browser tab
  2. content - The main content unique to this page
  3. css - 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.

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

  1. We create a custom template renderer that supports our layout structure
  2. We register a currentYear function to use in our templates
  3. We configure Echo to use our renderer
  4. 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

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

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

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

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

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

go
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

  1. Create a layout with a sidebar that shows different navigation links based on whether the user is logged in
  2. Implement a mobile-responsive layout that changes structure on smaller screens
  3. Create a specialized layout for a blog section of your website with recent posts in the sidebar
  4. Add dark mode support to your layouts with a toggle button that persists the user's preference
  5. 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! :)