1
0
gomponents/README.md
Markus Wüstenberg 794c3b26ac
Render correct HTML5 (#44)
Previously, elements of kind void and empty elements generally would be rendered auto-closing (with a final `/` character in the start tag), which is allowed sometimes but arguably wrong. See https://dev.w3.org/html5/spec-LC/syntax.html#end-tags

This created problems with for example `textarea` and `script`, which cannot be auto-closing, or the browser renders it wrong.

Also clarified in the docs that this library outputs HTML5.

Fixes #42.
2020-11-16 12:38:24 +01:00

3.2 KiB

gomponents

GoDoc codecov

gomponents are declarative view components in Go, that can render to HTML5. gomponents aims to make it easy to build HTML5 pages of reusable components, without the use of a template language. Think server-side-rendered React, but without the virtual DOM and diffing.

The implementation is still incomplete, but usable. The API may change until version 1 is reached.

Check out the blog post gomponents: declarative view components in Go for background.

Features

  • Write declarative HTML5 in Go without all the strings, so you get
    • Type safety
    • Auto-completion
    • Nice formatting with gofmt
  • Simple API that's easy to learn and use
  • Build reusable view components
  • No external dependencies

Usage

Get the library using go get:

go get -u github.com/maragudk/gomponents

Then do something like this:

package main

import (
	"net/http"

	g "github.com/maragudk/gomponents"
	"github.com/maragudk/gomponents/attr"
	"github.com/maragudk/gomponents/el"
)

func main() {
	_ = http.ListenAndServe("localhost:8080", handler())
}

func handler() http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		page := Page("Hi!", r.URL.Path)
		_ = page.Render(w)
	}
}

func Page(title, path string) g.Node {
	return el.Document(
		el.HTML(
			g.Attr("lang", "en"),
			el.Head(
				el.Title(title),
				el.Style(g.Attr("type", "text/css"), g.Raw(".is-active{font-weight: bold}")),
			),
			el.Body(
				Navbar(path),
				el.H1(title),
				el.P(g.Textf("Welcome to the page at %v.", path)),
			),
		),
	)
}

func Navbar(path string) g.Node {
	return g.El("nav",
		el.A("/", attr.Classes{"is-active": path == "/"}, g.Text("Home")),
		el.A("/about", attr.Classes{"is-active": path == "/about"}, g.Text("About")),
	)
}

You could also use a page template to simplify your code a bit:

package main

import (
	"net/http"

	g "github.com/maragudk/gomponents"
	"github.com/maragudk/gomponents/attr"
	c "github.com/maragudk/gomponents/components"
	"github.com/maragudk/gomponents/el"
)

func main() {
	_ = http.ListenAndServe("localhost:8080", handler())
}

func handler() http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		page := Page("Hi!", r.URL.Path)
		_ = page.Render(w)
	}
}

func Page(title, path string) g.Node {
	return c.HTML5(c.DocumentProps{
		Title:       title,
		Language:    "en",
		Head:        []g.Node{el.Style(g.Attr("type", "text/css"), g.Raw(".is-active{font-weight: bold}"))},
		Body:        []g.Node{
			Navbar(path),
			el.H1(title),
			el.P(g.Textf("Welcome to the page at %v.", path)),
		},
	})
}

func Navbar(path string) g.Node {
	return g.El("nav",
		el.A("/", attr.Classes{"is-active": path == "/"}, g.Text("Home")),
		el.A("/about", attr.Classes{"is-active": path == "/about"}, g.Text("About")),
	)
}

For more complete examples, see the examples directory.