π Templates
Templates render dynamic content without requiring a separate frontend framework.
Template Enginesβ
Fiber accepts a custom template engine during app initialization.
app := fiber.New(fiber.Config{
// Provide a template engine
Views: engine,
// Default layout for views, overridden by the layouts passed to Render()
ViewsLayout: "layouts/main",
// Enables/Disables access to `ctx.Locals()` entries in rendered views
// (defaults to false)
PassLocalsToViews: false,
})
Supported Enginesβ
Fiber maintains a templates package that wraps several engines:
Custom engines implement the Views interface to work with Fiber.
type Views interface {
// Fiber executes Load() in fiber.New() to load/parse the templates;
// an error is logged as a warning, not returned
Load() error
// Outputs a template to the provided buffer using the provided template,
// template name, and bound data
Render(io.Writer, string, any, ...string) error
}
The Render method powers ctx.Render(), which accepts a template name and data to bind.
Rendering Templatesβ
After configuring an engine, handlers call ctx.Render() with a template name and data to send the rendered output.
func (c fiber.Ctx) Render(name string, bind any, layouts ...string) error
By default, ctx.Render() wraps the template in the layout named by ViewsLayout. Pass layout names in the layouts argument to use another layout for that call.
Without a Views engine, or with a nil engine pointer, Render reads the file at name and parses it with Go's html/template.
- Example
- layouts/index.html
app.Get("/", func(c fiber.Ctx) error {
return c.Render("index", fiber.Map{
"Title": "Hello, World!",
})
})
<!DOCTYPE html>
<html>
<body>
<h1>{{.Title}}</h1>
</body>
</html>
When PassLocalsToViews is enabled, all values set using ctx.Locals(key, value) are passed to the template. Use unique keys to avoid collisions.
Locals, and the values you add with c.ViewBind, are merged into the data only when you pass a fiber.Map or nil to Render. A key in the map you pass wins over a local or a ViewBind value with the same name. The merge goes into a copy, so the map you pass is left unchanged and can be shared across requests.
Rendering Without a Requestβ
app.Render writes a template through the configured engine to any io.Writer, applying ViewsLayout when you pass no layout. app.ReloadViews runs Load again on the engine of the app and of every mounted sub-app, for example after a template changed on disk, and returns fiber.ErrNoViewEngineConfigured only when none of them has an engine. app.Render returns it when the app itself has none. A nil engine pointer counts as none for both.
Mounted Appsβ
A sub-app mounted with app.Use renders its templates with its own Views for requests under its mount path. A sub-app without an engine falls back to the nearest app above it that has one. The default layout is the ViewsLayout of the innermost app that sets one, and the search stops at the app whose engine renders the template. This needs a literal mount path, taken from the top-level app down: under a parameter, as in app.Use("/t/:tenant", tenantApp), the routes still match and run but the Views and ViewsLayout of the sub-app, and of the apps mounted inside it, are not used. The nearest enclosing mount with a literal path supplies them, or the top-level app when there is none, and without an engine anywhere Render reads name as a file path. A sub-app mounted through Domain matches a parameterized mount path as a pattern and keeps its own Views. PassLocalsToViews is read from the top-level app, whatever the sub-app sets.
Advanced Templatingβ
Custom Functionsβ
Fiber supports adding custom functions to templates.
AddFuncβ
Adds a global function to all templates.
func (e *Engine) AddFunc(name string, fn any) IEngineCore
- AddFunc Example
- views/index.html
// Add `ToUpper` to engine
engine := html.New("./views", ".html")
engine.AddFunc("ToUpper", func(s string) string {
return strings.ToUpper(s)
})
// Initialize Fiber App
app := fiber.New(fiber.Config{
Views: engine,
})
app.Get("/", func (c fiber.Ctx) error {
return c.Render("index", fiber.Map{
"Content": "hello, World!",
})
})
<!DOCTYPE html>
<html>
<body>
<p>This will be in {{ToUpper "all caps"}}:</p>
<p>{{ToUpper .Content}}</p>
</body>
</html>
AddFuncMapβ
Adds a Map of functions (keyed by name) to all templates.
func (e *Engine) AddFuncMap(m map[string]any) IEngineCore
- AddFuncMap Example
- views/index.html
// Add `ToUpper` to engine
engine := html.New("./views", ".html")
engine.AddFuncMap(map[string]any{
"ToUpper": func(s string) string {
return strings.ToUpper(s)
},
})
// Initialize Fiber App
app := fiber.New(fiber.Config{
Views: engine,
})
app.Get("/", func (c fiber.Ctx) error {
return c.Render("index", fiber.Map{
"Content": "hello, world!",
})
})
<!DOCTYPE html>
<html>
<body>
<p>This will be in {{ToUpper "all caps"}}:</p>
<p>{{ToUpper .Content}}</p>
</body>
</html>
- For more advanced template documentation, please visit the gofiber/template GitHub Repository.
Full Exampleβ
- Example
- views/index.html
package main
import (
"log"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/template/html/v2"
)
func main() {
// Initialize standard Go html template engine
engine := html.New("./views", ".html")
// If you want to use another engine,
// just replace with following:
// Create a new engine with django
// engine := django.New("./views", ".django")
app := fiber.New(fiber.Config{
Views: engine,
})
app.Get("/", func(c fiber.Ctx) error {
// Render index template
return c.Render("index", fiber.Map{
"Title": "Go Fiber Template Example",
"Description": "An example template",
"Greeting": "Hello, World!",
});
})
log.Fatal(app.Listen(":3000"))
}
<!DOCTYPE html>
<html>
<head>
<title>{{.Title}}</title>
<meta name="description" content="{{.Description}}">
</head>
<body>
<h1>{{.Title}}</h1>
<p>{{.Greeting}}</p>
</body>
</html>