Skip to main content

Template Engines

ยท 4 min read
Fiber Team
Maintainers

Not every project is a JSON API. Sometimes you need to render HTML - a landing page, an admin panel, an email template, a server-rendered app. Fiber supports 9 template engines through a single Views interface, so you pick the syntax you like and Fiber handles the rest.

The surprising part is not the number of engines. It is that switching from one engine to another requires changing exactly two lines of code: the import and the engine constructor. Your handlers, layouts, and rendering calls stay the same.

The Views Interfaceโ€‹

Every template engine implements Fiber's Views interface. Your handlers call c.Render() and Fiber delegates to whatever engine you configured:

app.Get("/", func(c fiber.Ctx) error {
return c.Render("index", fiber.Map{
"Title": "Welcome",
"Items": items,
})
})

This handler works identically whether you are using HTML templates, Pug, Django, or any other engine. The engine is configured once at startup and never referenced in handler code.

The 9 Enginesโ€‹

EngineSyntax StyleBest For
htmlGo's html/templateGo developers, maximum control
django{% block %} / {{ var }}Python developers, designers
pugIndentation-based, minimalClean markup, rapid prototyping
handlebars{{#each}} / {{> partial}}JavaScript developers
mustacheLogic-less {{var}}Simple templates, emails
jetGo-native, fastPerformance-critical rendering
amberHAML-like, Go functionsRuby developers
aceIndentation-based, Go funcsClean Go-native templates
slimMinimal whitespace syntaxRuby/Slim developers

Quick Start with html/templateโ€‹

The most common choice - Go's built-in template engine with Fiber integration:

import (
"github.com/gofiber/fiber/v3"
"github.com/gofiber/template/html/v2"
)

func main() {
// Point to your template directory and file extension
engine := html.New("./views", ".html")

app := fiber.New(fiber.Config{
Views: engine,
})

app.Get("/", func(c fiber.Ctx) error {
return c.Render("index", fiber.Map{
"Title": "My App",
})
})

app.Listen(":3000")
}

With a template file at ./views/index.html:

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

Using Layoutsโ€‹

Layouts let you define a shared structure (header, footer, navigation) and inject page content into it:

app.Get("/", func(c fiber.Ctx) error {
return c.Render("index", fiber.Map{
"Title": "Home",
}, "layouts/main")
})

The layout file ./views/layouts/main.html:

<!DOCTYPE html>
<html>
<head><title>{{.Title}}</title></head>
<body>
<nav><!-- shared navigation --></nav>
{{embed}}
<footer><!-- shared footer --></footer>
</body>
</html>

The {{embed}} tag marks where the page content is inserted. The page template (index.html) contains only the page-specific content.

Switching Enginesโ€‹

Want to use Pug instead of HTML templates? Change two lines:

import "github.com/gofiber/template/pug/v2"

engine := pug.New("./views", ".pug")

Your handlers stay the same. Your layouts stay the same. Only the template files and the engine constructor change.

A Pug template for the same page:

h1= Title
p Welcome to #{Title}

Development Featuresโ€‹

Every engine supports features that make development easier:

engine := html.New("./views", ".html")

// Reload templates on every request (development only)
engine.Reload(true)

// Print parsed template names for debugging
engine.Debug(true)

// Add custom template functions
engine.AddFunc("upper", strings.ToUpper)
engine.AddFunc("formatDate", func(t time.Time) string {
return t.Format("2006-01-02")
})

Reload(true) is essential during development - without it, you need to restart the server to see template changes. Disable it in production for performance.

Embedding Templates in the Binaryโ€‹

For production deployments, you can embed templates into your Go binary using embed.FS:

import "embed"

//go:embed views/*
var viewsFS embed.FS

engine := html.NewFileSystem(http.FS(viewsFS), ".html")

This eliminates the need to ship template files alongside your binary. The templates are compiled into the executable, making deployment a single file copy.

When to Use Which Engineโ€‹

  • Building a Go-native app? Use html - it is the standard, has the best tooling support, and is fast.
  • Working with frontend developers who know Django/Jinja2? Use django - the syntax is familiar.
  • Need clean, minimal markup? Use pug - indentation-based syntax reduces boilerplate.
  • Rendering emails or simple content? Use mustache - logic-less templates force separation of concerns.
  • Maximum rendering performance? Use jet - benchmarks show it is consistently fast.

Internal Referencesโ€‹