No description
Find a file
Delaney 9414832bf3
Merge pull request #16 from michaeldebetaz/popover
fix: mispelling of "popover"
2025-11-12 06:05:43 -08:00
.task/checksum don't build iconify 2024-11-13 20:55:45 -08:00
.vscode Move to template/text to allow for dynamic templates 2023-12-19 20:23:39 -08:00
cfg fix: mispelling of "popover" 2025-11-07 15:56:42 +01:00
cmd/gen don't build iconify 2024-11-13 20:55:45 -08:00
docs initial commit 2023-12-19 12:49:53 -08:00
elements chore: generated stuffs 2025-11-07 15:57:12 +01:00
generator update icons 2024-05-20 15:20:25 -07:00
tests docs: updating the README to use ElementRenderer instead of ElementBuilder 2024-05-18 11:56:34 -05:00
.gitattributes fix datastar view transition api 2024-01-04 09:45:14 -08:00
.gitignore don't build iconify 2024-11-13 20:55:45 -08:00
go.mod update iconify, add dynamic builders 2024-03-23 12:04:52 -07:00
go.sum update iconify, add dynamic builders 2024-03-23 12:04:52 -07:00
LICENSE initial commit 2023-12-19 12:49:53 -08:00
README.md docs: updating the README to use ElementRenderer instead of ElementBuilder 2024-05-18 11:56:34 -05:00
Taskfile.yml don't build iconify 2024-11-13 20:55:45 -08:00

GoDoc Go Report Card

Gostar

A fluent HTML builder for Go built directly from the HTML Living Standard.

mascot

What does it do?

Instead of creating HTML using string concatenation or templates, Gostar allows you to create HTML using a fluent API. This allows you to create HTML in a more natural way, and makes it easier to create HTML programmatically. It does that by building directly from the HTML Living Standard and introspecting to creating automated conversions for all HTML elements and attributes.

Just import the package. I prefer dot imports so I don't have to type the package name over and over. All HTML tags and attributes are uppercase to avoid collisions with Go keywords and make explicit what is a DSL.

import . "github.com/delaneyj/gostar/elements"

Since everything is just fluent functions you can compose them together to create more complex HTML structures. You can also create your own fluent functions to create reusable HTML components.

// A couple common structs that could be models in your
// application.
type (
	Navigation struct {
		Link string
		Item string
	}

	User struct {
		FirstName      string
		RawContent     string
		EscapedContent string
	}
)

// ElementRenderer functions for HTML portions of your
// application.
var (
	header = func(title string) ElementRenderer {
		return HEADER(
			TITLE().TextF("%s's Home Page", title),
			DIV().CLASS("header").Text("Page Header"),
		)
	}

	navigation = func(nav []*Navigation) ElementRenderer {
		return NAV(
			UL(
				Range(nav, func(n *Navigation) ElementRenderer {
					return LI(
						A().HREF(n.Link).Text(n.Item),
					)
				}),
			).CLASS("navigation"),
		)
	}

	footer = func() ElementRenderer {
		return FOOTER(DIV().CLASS("footer").Text("copyright 2016"))
	}

	index = func(u *User, nav []*Navigation, title string) ElementRenderer {
		return Group(
			Text("<!DOCTYPE html>"),
			HTML(
				BODY(
					header(title),
					navigation(nav),
					SECTION(
						DIV(
							DIV(
								H4().TextF("Hello %s", u.FirstName),
								DIV().CLASS("raw").Text(u.RawContent),
								DIV().CLASS("enc").Escaped(u.EscapedContent),
							).CLASS("welcome"),
							Range(lo.Range(5), func(i int) ElementRenderer {
								count := i + 1
								return Tern(
									count == 1,
									P().TextF("%s has %d message", u.FirstName, count),
									P().TextF("%s has %d messages", u.FirstName, count),
								)
							}),
						).CLASS("content"),
					),
					footer(),
				),
			),
		)
	}
)

// An index handler to render the generated index page.
func indexHandler(w http.ResponseWriter, r *http.Request) {
	user := &User{
		FirstName:      "John",
		RawContent:     `<a href="http://john.com">This is some raw content</a>`,
		EscapedContent: `&lt;a href=&#34;http://john.com&#34;&gt;This is some encoded content&lt;/a&gt;`,
	}

	nav := []*Navigation{
		{Link: "/home", Item: "Home"},
		{Link: "/about", Item: "About"},
	}

	index(user, nav, "John").Render(w)
}

Performance

Testing shows it's faster than Go builtin template/html package but this is including creating and writing to a buffer. It's still sub-microsecond for most pages and the convenience of the fluent API is worth it. Avoiding the parse, setup template context, then execute loop mean way less work for the developer.