¶Generating HTML from Go
A variety of techniques for generating HTML content from Go.
¶Templating
The canonical approach is to use templates, primarily the html/template stdlib package. The “Let's Go” book leans heavily on this approach.
“Let's Go” Web development ebook
There are other templating languages that you can use, too. There are dozens of implementations of Mustache templating, and at least one robust implementation of Jinja templates. (Though this is much less popular — if you're going for the templating route, html/template is by far the most paved path.)
For all of these approaches, the templates are stored out-of-band in separate files. (Though note recent best practice has been to embed those files into your executable using go:embed, so that the individual template files don't need to be distributed separately.)
¶JSX-style
Another popular choice is templ, which implements JSX-style templating.
Here, the templates are not stored in separate files, but are inlined directly into your Go code. That requires a syntax extension, with (e.g.) HTML elements appearing directly in the source code:
templ Hello(name string) { <div>Hello, { name }</div> } templ Greeting(person Person) { <div class="greeting"> @Hello(person.Name) </div> }
A go:generate preprocessor translates this into a Go file, which is then compiled along with the rest of your code. In the case of templ, each template is translated into a Go function (or method) that returns a Component, which is just something that knows how to render itself into an io.Writer.
¶Fluent APIs
The benefit of the JSX-style approach is that the HTML portions of your templates continue to look like actual HTML. The main drawback is that your templ files aren't (pure) Go files, so existing tooling doesn't work on them.
Fluent APIs decide to resolve that trade-off in the other direction, by providing pure Go libraries that you call from regular Go code. The gomponents equivalent of the above example is:
import . "maragu.dev/gomponents" import . "maragu.dev/gomponents/html" func Hello(name string) Node { return Div(Text("Hello, " + name)) } func Greeting(person Person) Node { return Div( Class("greeting"), Hello(person.Name), ) }
Note that this is largely the same API shape as templ. (The Component and Node interfaces are basically identical — something that “knows how to render itself into an io.Writer.) But instead of translating a JSX-like syntax into this function, you just write it directly.
There are several takes on this idea: