Skip to content

alesr/templator

Repository files navigation

Templator

codecov Go Report Card Go Reference Go Version License: MIT Latest Release

A type-safe HTML template rendering engine for Go.

Table of Contents

Problem Statement

Go's built-in html/template package is great, but it has one big weakness: no type safety.

tmpl := template.Must(template.ParseFiles("home.html")) // this compiles fine... err := tmpl.Execute(w, struct { WrongField string MissingStuff int }{})

You only find out at runtime that your data does not match what the template expects: missing fields, typos, wrong types, and so on.

What Templator Solves

Templator wraps html/template and gives you compile-time guarantees with generics.

You define your data once:

type HomeData struct { Title string Content string }

Then template execution is type-checked against the template by the compiler.

You can get a handler in two equivalent ways:

  1. Simple and direct: reg.Get("home")
  2. IDE-friendly codegen: tpl.GetHome()

Both return *Handler[T] with the same behavior. The difference is only developer experience.

Features

  • Compile-time type safety via generics
  • Optional field validation (catches mismatches when loading templates)
  • Concurrent-safe template management with fs.FS support
  • Custom template functions
  • Context cancellation and deadline propagation
  • Minimal overhead on top of html/template
  • Small API with optional code generation helpers

Installation

go get github.com/alesr/templator@latest

Optional generator binary:

go install github.com/alesr/templator/cmd/generate@latest

Quick Start

package main import ( "context" "log" "os" "github.com/alesr/templator" ) type HomeData struct { Title string Content string } func main() { fs := os.DirFS(".") reg, err := templator.NewRegistry[HomeData](fs) if err != nil { log.Fatal(err) } home, err := reg.Get("home") if err != nil { log.Fatal(err) } err = home.Execute(context.Background(), os.Stdout, HomeData{ Title: "Welcome", Content: "Hello, World!", }) if err != nil { log.Fatal(err) } }

How It All Works Together

  1. Define a data struct (this is your type parameter T).
  2. Create a Registry[T] for your filesystem.
  3. Get a Handler[T] either with reg.Get("name") or generated accessors like tpl.GetName().
  4. Execute with handler.Execute(ctx, writer, data).
  5. Optionally enable field validation, custom functions, or a different templates path.

Everything is still powered by the standard html/template package.

Usage Examples

Type-Safe Templates (different data per template)

type HomeData struct{ Title, Content string } type AboutData struct{ Company string; Year int } homeReg, _ := templator.NewRegistry[HomeData](fs) aboutReg, _ := templator.NewRegistry[AboutData](fs) home, _ := homeReg.Get("home") about, _ := aboutReg.Get("about") home.Execute(ctx, w, HomeData{...}) // ok home.Execute(ctx, w, AboutData{...}) // trying to pass about data to the home tmpl is caught by the compiler

Custom Template Functions

funcMap := template.FuncMap{ "upper": strings.ToUpper, } reg, _ := templator.NewRegistry[PageData]( fs, templator.WithTemplateFuncs[PageData](funcMap), )

Use in templates as usual: {{.Title | upper}}

File System Support

// Embedded FS //go:embed templates/* var embedFS embed.FS reg, _ := templator.NewRegistry[HomeData](embedFS) // os dir reg, _ = templator.NewRegistry[HomeData](os.DirFS("templates")) // in-memory for tests fsys := fstest.MapFS{ /* ... */ } reg, _ = templator.NewRegistry[HomeData](fsys)

Field Validation (catches errors early)

type ArticleData struct { Title string Content string } reg, _ := templator.NewRegistry[ArticleData]( fs, templator.WithFieldValidation(ArticleData{}), )

If a template references {{.Author}} but Author does not exist in ArticleData, Get(...) returns a validation error.

Template Generation

Want tpl.GetHome() instead of string lookup? Use the generator.

go run github.com/alesr/templator/cmd/generate \ -package main \ -templates ./templates \ -out ./templator_accessors_gen.go

You can also wire it into go generate:

//go:generate go run github.com/alesr/templator/cmd/generate -package main -templates ./templates -out ./templator_accessors_gen.go

Then use the generated wrapper:

reg, _ := templator.NewRegistry[HomeData](fs) tpl := NewTemplateAccessors(reg) home, _ := tpl.GetHome() header, _ := tpl.GetComponentsHeader()

components/header.html maps to GetComponentsHeader.

For full generator docs (flags, behavior, and examples), see cmd/generate/README.md.

Configuration

reg, err := templator.NewRegistry[HomeData]( fs, templator.WithTemplatesPath[HomeData]("views"), templator.WithTemplateFuncs[HomeData](funcMap), templator.WithFieldValidation(HomeData{}), )

Development Requirements

  • Go 1.24 or higher

Contributing

Contributions are welcome.

License

MIT

About

Type-safe HTML template rendering engine for Go with generics, compile-time validation and go-generate support

Topics

Resources

License

Stars

Watchers

Forks

Contributors

Languages