Skip to main content
Fiber supports template engines for server-side rendering through the Views interface. This allows you to render HTML templates with dynamic data.

How Templates Work

Fiber uses a Views interface that template engines implement. You configure the engine when creating your app:
app := fiber.New(fiber.Config{
    Views: engine,  // Your template engine
})
Then render templates in your handlers:
app.Get("/", func(c fiber.Ctx) error {
    return c.Render("index", fiber.Map{
        "Title": "Homepage",
        "User":  "John",
    })
})

Views Interface

The Views interface requires two methods:
type Views interface {
    Load() error
    Render(out io.Writer, name string, binding any, layout ...string) error
}
  • Load(): Load/compile templates (called during app initialization)
  • Render(): Render a template with data

Template Engines

Fiber works with multiple template engines through the template middleware package.

Supported Engines

html

Go’s built-in html/template

django

Django-style templates

handlebars

Handlebars templates

mustache

Mustache templates

pug

Pug (formerly Jade)

amber

Amber templates

jet

Jet templates

ace

Ace templates

Using html/template

The most common engine is Go’s built-in html/template:
go get github.com/gofiber/template/html/v2

Basic Setup

package main

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

func main() {
    // Create template engine
    engine := html.New("./views", ".html")
    
    // Create app with views
    app := fiber.New(fiber.Config{
        Views: engine,
    })
    
    app.Get("/", func(c fiber.Ctx) error {
        return c.Render("index", fiber.Map{
            "Title": "Homepage",
        })
    })
    
    app.Listen(":3000")
}

Template File

views/index.html
<!DOCTYPE html>
<html>
<head>
    <title>{{.Title}}</title>
</head>
<body>
    <h1>{{.Title}}</h1>
    <p>Welcome to Fiber!</p>
</body>
</html>

Template Configuration

Directory Structure

project/
├── main.go
└── views/
    ├── index.html
    ├── about.html
    └── layouts/
        └── main.html

Engine Options

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

// Reload templates on each render (development)
engine.Reload(true)

// Enable debug mode
engine.Debug(true)

// Add custom functions
engine.AddFunc("upper", func(s string) string {
    return strings.ToUpper(s)
})

Layouts

Use layouts for shared template structure:

Layout Template

views/layouts/main.html
<!DOCTYPE html>
<html>
<head>
    <title>{{.Title}}</title>
</head>
<body>
    <nav>
        <a href="/">Home</a>
        <a href="/about">About</a>
    </nav>
    
    {{embed}}
    
    <footer>&copy; 2024</footer>
</body>
</html>

Using Layouts

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

Default Layout

Set a global default layout:
app := fiber.New(fiber.Config{
    Views:       engine,
    ViewsLayout: "layouts/main",
})

app.Get("/", func(c fiber.Ctx) error {
    // Uses default layout
    return c.Render("index", fiber.Map{
        "Title": "Homepage",
    })
})

Passing Data

Using fiber.Map

app.Get("/user/:id", func(c fiber.Ctx) error {
    return c.Render("user", fiber.Map{
        "Name":  "John Doe",
        "Email": "[email protected]",
        "ID":    c.Params("id"),
    })
})

Using Structs

type User struct {
    Name  string
    Email string
}

app.Get("/user", func(c fiber.Ctx) error {
    user := User{
        Name:  "John Doe",
        Email: "[email protected]",
    }
    
    return c.Render("user", user)
})

Passing Locals

Automatically pass c.Locals() to templates:
app := fiber.New(fiber.Config{
    Views:             engine,
    PassLocalsToViews: true,
})

app.Use(func(c fiber.Ctx) error {
    c.Locals("AppName", "My App")
    c.Locals("Year", 2024)
    return c.Next()
})

app.Get("/", func(c fiber.Ctx) error {
    // AppName and Year are available in template
    return c.Render("index", fiber.Map{})
})

ViewBind

Pre-bind data for templates:
app.Get("/", func(c fiber.Ctx) error {
    // Add common data
    c.ViewBind(fiber.Map{
        "Title":   "Homepage",
        "AppName": "My App",
    })
    
    // Render - ViewBind data is included
    return c.Render("index", fiber.Map{
        "Content": "Welcome!",
    })
})

Template Functions

Add custom functions to templates:
engine := html.New("./views", ".html")

// Add function
engine.AddFunc("upper", strings.ToUpper)

engine.AddFunc("formatDate", func(t time.Time) string {
    return t.Format("2006-01-02")
})

engine.AddFunc("add", func(a, b int) int {
    return a + b
})

Using Functions

<h1>{{upper .Title}}</h1>
<p>Date: {{formatDate .Date}}</p>
<p>Total: {{add .A .B}}</p>

Reloading Templates

Enable auto-reload during development:
engine := html.New("./views", ".html")

// Reload templates on each render
engine.Reload(true)

app := fiber.New(fiber.Config{
    Views: engine,
})
Or reload manually:
app.Get("/reload", func(c fiber.Ctx) error {
    if err := app.ReloadViews(); err != nil {
        return err
    }
    return c.SendString("Templates reloaded")
})

Template Syntax Examples

Variables

<h1>{{.Title}}</h1>
<p>{{.Description}}</p>

Conditionals

{{if .User}}
    <p>Welcome, {{.User.Name}}!</p>
{{else}}
    <p>Please log in</p>
{{end}}

Loops

<ul>
{{range .Items}}
    <li>{{.Name}} - ${{.Price}}</li>
{{end}}
</ul>

Nested Data

<h2>{{.User.Name}}</h2>
<p>{{.User.Profile.Bio}}</p>

Error Handling

Handle template rendering errors:
app.Get("/", func(c fiber.Ctx) error {
    if err := c.Render("index", fiber.Map{"Title": "Home"}); err != nil {
        return c.Status(500).SendString("Template error: " + err.Error())
    }
    return nil
})

Using Other Template Engines

Pug

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

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

app := fiber.New(fiber.Config{
    Views: engine,
})
views/index.pug
doctype html
html
  head
    title= Title
  body
    h1= Title
    p Welcome to Fiber!

Handlebars

go get github.com/gofiber/template/handlebars/v2
import "github.com/gofiber/template/handlebars/v2"

engine := handlebars.New("./views", ".hbs")

app := fiber.New(fiber.Config{
    Views: engine,
})
views/index.hbs
<!DOCTYPE html>
<html>
<head>
    <title>{{Title}}</title>
</head>
<body>
    <h1>{{Title}}</h1>
    {{#if User}}
        <p>Welcome, {{User.Name}}!</p>
    {{/if}}
</body>
</html>

Best Practices

Define shared structure in layouts to avoid duplication.
app := fiber.New(fiber.Config{
    ViewsLayout: "layouts/main",
})
Use template reloading during development for faster iteration.
engine.Reload(true)  // Development only
Group related templates in subdirectories.
views/
├── users/
│   ├── list.html
│   └── profile.html
└── posts/
    ├── list.html
    └── detail.html
Disable reloading in production for better performance.
engine.Reload(false)  // Production

See Also

Context

Render templates from handlers

Template Package

Official template engines

Rendering

c.Render() API reference

Static Files

Serve static assets

Build docs developers (and LLMs) love