Generate type-safe (ish) wrapper code for Go HTML templates.
Take a look at the
example
directory for a full example ortests
for a range of supported features
- Render templates partials
template.HTML
- Render templates to
io.Writer
orhttp.ResponseWriter
- Generate data structs (props) for all templates and sub-templates
- Unfortunately
- Supports variables, loops, conditionals and sub-templates via static analysis.
- Hot-reloading: templates are loaded from disk during development, so changes reflect immediately
- IDE support: Use well-defined, well-supported languages. HTML, CSS, JS and Go text templates
- AI support: LLMs are incredibly familiar with HTML and very familiar with Go text templates
- Compile time errors for invalid templates or usage of templates
go install github.com/fritzkeyzer/gohtml/cmd/[email protected]
- Create
gohtml.yaml
:
version: "0.1.3"
directories:
- path: "app/pages"
- path: "app/components"
- Run generator
gohtml
components.gohtml
:
{{define "PersonCard"}}
<div class="card">
<h3>{{.Name}}</h3>
<p>{{.Age}} - {{.Email}}</p>
<span>
{{range .Interest}}
<sm>{{.}}</sm>
{{else}}
<sm>no interests recorded</sm>
{{end}}
</span>
</div>
{{end}}
Generated code:
type PersonCardData struct {
Name any
Age any
Email any
Interest []PersonCardInterestItem
}
type PersonCardInterestItem struct {
any
}
// PersonCard renders the "PersonCard" template as an HTML fragment
func PersonCard(data PersonCardData) template.HTML
// RenderPersonCard renders the "PersonCard" template to a writer
func RenderPersonCard(w io.Writer, data PersonCardData) error
// RenderPersonCardHTTP renders PersonCard to an http.ResponseWriter
func RenderPersonCardHTTP(w http.ResponseWriter, data PersonCardData) error
💡Look at the
tests
andexample
directories for more advanced examples
- Additional install options
- Go type annotations
- Multiple components per file
- Component reuse with typed variables
- Template caching
- Configurable output location
- YAML configuration
- Root context selector support
- HTTP rendering with error handling
- If a .gohtml file only contains sub templates, a render function (for the file) is still created even if it will do nothing
Issues and PRs welcome!
Please report errors and if it's possible, create a test case for your error and submit a PR. I would greatly appreciate it.
- Fix range elements with only {{.}} children
- Fix missing else branches
- Use pointers for nested data to support passing nils
- Improve type detection when a variable appears multiple times eg, {{if .Data}} {{template "Component" .Data}} {{end}}
- No longer generate functions for empty templates (eg: files that only have sub-template definitions)
- Fix deeply nested template directories causing bad generation
- Fix generation for conditionals with operators (not, eq, etc)
- Define multiple template components per file (sub-templates can be reused within the same package)
- Create:
{{define "component"}} ... {{end}}
- Reuse:
{{template "component"}}
- Use data:
{{template "person" .PersonData}}
- Create:
- Generate a single file per directory:
gohtml.gen.go
- LiveReload with env var:
GOHTML_LIVERELOAD
- Can be set manually if needed eg:
views.LiveReload = (env == "local")
)
- Can be set manually if needed eg:
- CLI: improved debug logs
- Updated logic for naming generated loop structs
- Added a golden file test, that tests the entire
tests
directory - Removed RenderHTTP error handler
- Support
$
root context selector - Fix variables nested within conditionals bug
- Add RenderHTTP function with configurable error handler
- Fix generated filepath bug
- Simplified config
- Fix superfluous type definitions
- Add more tests, including parsing and generation
- Apply standard go formatting to generated code
- Added yaml config support.
- By default, gohtml checks for a file alongside it:
gohtml.yaml
otherwise the config file can be specified with the-c
flag. - The
-d
and-f
flags have been removed in favour of using a config file. - Known issue: When generating types and a loop is involved - an unused type is generated. The failing test:
TestTemplate_Generate/tests/person.gohtml
captures this issue.
Initial version supports:
- variables and nested variables, eg:
{{ .Name }}
or{{ .User.Location.City }}
- conditionals eg:
{{ if .IsSignedIn }} ... {{ else }} ... {{ end }}
- loops eg:
{{range $link := .Socials}} <a href="{{ $link.Href }}">{{ $link.Name }}</li> {{end}}