Template Files

GoHT templates live in .goht files. A .goht file is mostly Go source: it can declare a package, imports, helper types, helper functions, and one or more template declarations.

Template declarations start with a language directive followed by a valid Go function declaration:

  • @haml declares a Haml template.
  • @slim declares a Slim template.
  • @ego declares an EGO template.
package pages

import "time"

type User struct {
	Name string
}

func formattedDate(t time.Time) string {
	return t.Format("Jan 2, 2006")
}

@haml Hello(user User, joinedAt time.Time) {
	%p Hello, #{user.Name}!
	%small Joined #{formattedDate(joinedAt)}
}

GoHT generates a Go function for each template declaration. The template name must therefore be a valid Go function name, and parameters use normal Go function parameter syntax.

func Hello(user User, joinedAt time.Time) goht.Template {
	return goht.TemplateFunc(func(ctx context.Context, __w io.Writer) (__err error) {
		// generated rendering code
	})
}

The generated function returns a goht.Template. A goht.Template renders with a context.Context and an io.Writer.

func handler(w http.ResponseWriter, r *http.Request) {
	err := Hello(user, time.Now()).Render(r.Context(), w)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

Inside a template, the render context is available as ctx. Template code should write output through template syntax rather than writing to the io.Writer directly.

Template directives

Use the directive that matches the template language you want to write.

@haml HamlGreeting(name string) {
	%p Hello, #{name}!
}
@slim SlimGreeting(name string) {
	p Hello, #{name}!
}
@ego EgoGreeting(name string) {
	<p>Hello, <%= name %>!</p>
}

Deprecated @goht

@goht is still accepted as a deprecated alias for @haml. Existing Haml templates that use @goht can migrate by changing only the declaration directive:

@goht Greeting(name string) {
	%p Hello, #{name}!
}

becomes:

@haml Greeting(name string) {
	%p Hello, #{name}!
}

Use @haml in new code and documentation examples.

Multiple templates per file

A single .goht file can contain multiple templates. Haml, Slim, and EGO templates can also coexist in the same file.

package views

import "github.com/example/app/domain"

@haml Page(title string) {
	!!!
	%html
		%head
			%title= title
		%body
			=@children
}

@slim Nav(items []string) {
	nav
		ul
			- for _, item := range items
				li= item
}

@ego StatusBadge(status domain.Status) {
	<span class="status"><%= status.String() %></span>
}

Each template must have a unique generated Go function name in the output package.

Receiver templates

Templates can also be declared as methods with Go receivers.

type ProfilePresenter struct {
	User User
}

@haml (p *ProfilePresenter) Greeting(name string) {
	%p #{p.User.Name} says "Hello, #{name}!"
}

GoHT generates the matching Go method:

func (p *ProfilePresenter) Greeting(name string) goht.Template {
	return goht.TemplateFunc(func(ctx context.Context, __w io.Writer) (__err error) {
		// generated rendering code
	})
}

Receiver templates render the same way as receiver-less templates:

presenter := &ProfilePresenter{User: user}
err := presenter.Greeting("World").Render(ctx, w)

Composition directives

Template composition uses @render, @children, and @slot. Dynamic attributes use @attributes inside Haml and Slim attribute lists.