go-notificationgo-notification
Features

Email Templates

Go html/text templates, compiled at app boot and rendered per-send.

For emails beyond simple line/action patterns, use Go's standard templates. The mail message builder has .Template(name, data) to render.

Setting up a template registry

main.go
import (
    "embed"
    "github.com/gopackx/go-notification/template"
)

//go:embed templates/*.html
var templatesFS embed.FS

registry, err := template.NewFromFS(templatesFS, "templates/*.html")
if err != nil { panic(err) }

notifier := notification.New(notification.Config{
    TemplateRegistry: registry,
})

Using a template in a notification

main.go
func (n Welcome) ToMail(u notification.Notifiable) *mail.Message {
    return mail.NewMessage().
        Subject("Welcome to the app, " + u.(User).Name).
        Template("welcome.html", map[string]any{
            "name":      u.(User).Name,
            "dashboard": "https://app.example.com/dashboard",
            "year":      time.Now().Year(),
        })
}

Template file example

index.html
<!-- templates/welcome.html -->
<!DOCTYPE html>
<html>
<body style="font-family: sans-serif">
  <h1>Welcome, {{ .name }}!</h1>
  <p>Click below to get started.</p>
  <p><a href="{{ .dashboard }}">Open dashboard</a></p>
  <hr>
  <small>© {{ .year }} Example Inc.</small>
</body>
</html>

HTML and text together

Good transactional email includes both HTML and plain-text versions. Use a .txt file alongside:

main.go
mail.NewMessage().
    Template("welcome.html", data).
    TextTemplate("welcome.txt", data)

The driver sends both as a multipart/alternative MIME message — clients choose which to render.

Layouts / partials

Standard Go template features work:

main.go
registry, _ := template.New().
    ParseFS(templatesFS, "templates/layouts/*.html", "templates/emails/*.html")

And in your email:

index.html
{{ define "welcome.html" }}
{{ template "layout.html" . }}
{{ end }}

{{ define "content" }}
<h1>Welcome, {{ .name }}</h1>
{{ end }}

Internationalization

Pass the locale into the data map and let your templates branch:

main.go
mail.NewMessage().Template("welcome.html", map[string]any{
    "name":   user.Name,
    "locale": user.Locale,
})

Or register separate templates per locale (welcome.en.html, welcome.id.html) and pick the name in ToMail.

External template engines

The TemplateRegistry is an interface. You can implement it with Handlebars, MJML (compile to HTML offline), or React Email (render Node → HTML at build time, embed as strings):

main.go
type engine interface {
    Render(name string, data any) (string, error)
}

type myRegistry struct { e engine }

func (r myRegistry) Render(name string, data any) (string, error) {
    return r.e.Render(name, data)
}

Template caching

Templates are parsed once at boot. For local development, you can wrap the registry in a dev-mode variant that re-parses on each render — but avoid that in production (adds latency and disk I/O per send).