Overview
A common task when creating custom routes or emails is the need of generating HTML output.
There are plenty of Go template-engines available that you can use for this, but often for simple cases
the Go standard library html/template
package should work just fine.
To make it slightly easier to load template files concurrently and on the fly, PocketBase also provides a
thin wrapper around the standard library in the
github.com/pocketbase/pocketbase/tools/template
utility package.
import "github.com/pocketbase/pocketbase/tools/template"
data := map[string]any{"name": "John"}
html, err := template.LoadFiles(
"views/base.html",
"views/partial1.html",
"views/partial2.html",
).Render(data)
The general flow when working with composed and nested templates is that you create "base" template(s)
that defines various placeholders using the
{{template "placeholderName" .}}
or
{{block "placeholderName" .}}default...{{end}}
actions.
Then in the partials, you define the content for those placeholders using the
{{define "placeholderName"}}custom...{{end}}
action.
The dot object (.
) in the above represents the data passed to the templates
via the Render(data)
method.
For more information about the template syntax please refer to the html/template and text/template package godocs. Another great resource is also the Hashicorp's Learn Go Template Syntax tutorial.
Example HTML page with layout
Consider the following app directory structure:
myapp/
views/
layout.html
hello.html
main.go
We define the content for layout.html
as:
<!DOCTYPE html>
<html lang="en">
<head>
<title>{{block "title" .}}Default app title{{end}}</title>
</head>
<body>
Header...
{{block "body" .}}
Default app body...
{{end}}
Footer...
</body>
</html>
We define the content for hello.html
as:
{{define "title"}}
Page 1
{{end}}
{{define "body"}}
<p>Hello from {{.name}}</p>
{{end}}
Then to output the final page, we'll register a custom /hello/:name
route:
// main.go
package main
import (
"log"
"net/http"
"github.com/labstack/echo/v5"
"github.com/pocketbase/pocketbase"
"github.com/pocketbase/pocketbase/apis"
"github.com/pocketbase/pocketbase/core"
"github.com/pocketbase/pocketbase/tools/template"
)
func main() {
app := pocketbase.New()
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
// this is safe to be used by multiple goroutines
// (it acts as store for the parsed templates)
registry := template.NewRegistry()
e.Router.GET("/hello/:name", func(c echo.Context) error {
name := c.PathParam("name")
html, err := registry.LoadFiles(
"views/layout.html",
"views/hello.html",
).Render(map[string]any{
"name": name,
})
if err != nil {
// or redirect to a dedicated 404 HTML page
return apis.NewNotFoundError("", err)
}
return c.HTML(http.StatusOK, html)
})
return nil
})
if err := app.Start(); err != nil {
log.Fatal(err)
}
}