gomponents are HTML components in pure Go. They render to HTML 5, and make it easy for you to build reusable components. So you can focus on building your app instead of learning yet another templating language.
$ go get maragu.dev/gomponents
Does your company depend on this project? Contact me at markus@maragu.dk to discuss options for a one-time or recurring invoice to ensure its continued thriving.
Into video? See this lightning talk from GopherCon 2021.
Have a look at this component. If you know HTML, you know what it does. Easy, right?
import (
. "maragu.dev/gomponents"
. "maragu.dev/gomponents/html"
)
func Navbar() Node {
return Nav(Class("navbar"),
Ol(
Li(A(Href("/"), Text("Home"))),
Li(A(Href("/contact"), Text("Contact"))),
Li(A(Href("/about"), Text("About"))),
),
)
}
Let's deduplicate a bit.
import (
. "maragu.dev/gomponents"
. "maragu.dev/gomponents/html"
)
func Navbar() Node {
return Nav(Class("navbar"),
Ol(
NavbarItem("Home", "/"),
NavbarItem("Contact", "/contact"),
NavbarItem("About", "/about"),
),
)
}
func NavbarItem(name, path string) Node {
return Li(A(Href(path), Text(name)))
}
Sometimes you only want to show a component based on some condition. Enter If
:
import (
. "maragu.dev/gomponents"
. "maragu.dev/gomponents/html"
)
func Navbar(loggedIn bool) Node {
return Nav(Class("navbar"),
Ol(
NavbarItem("Home", "/"),
NavbarItem("Contact", "/contact"),
NavbarItem("About", "/about"),
If(loggedIn,
NavbarItem("Log out", "/logout"),
),
),
)
}
func NavbarItem(name, path string) Node {
return Li(A(Href(path), Text(name)))
}
PS: There's also Iff
, which takes a callback function instead, to avoid those pesky nil pointer errors.
What if you have data and want to map it to components? No problem.
import (
. "maragu.dev/gomponents"
. "maragu.dev/gomponents/html"
)
type NavLink struct {
Name string
Path string
}
func Navbar(loggedIn bool, links []NavLink) Node {
return Nav(Class("navbar"),
Ol(
Map(links, func(l NavLink) Node {
return NavbarItem(l.Name, l.Path)
}),
If(loggedIn,
NavbarItem("Log out", "/logout"),
),
),
)
}
func NavbarItem(name, path string) Node {
return Li(A(Href(path), Text(name)))
}
Want to apply CSS classes based on state? Use the Classes
helper.
import (
. "maragu.dev/gomponents"
. "maragu.dev/gomponents/components"
. "maragu.dev/gomponents/html"
)
type NavLink struct {
Name string
Path string
}
func Navbar(loggedIn bool, links []NavLink, currentPath string) Node {
return Nav(Class("navbar"),
Ol(
Map(links, func(l NavLink) Node {
return NavbarItem(l.Name, l.Path, l.Path == currentPath)
}),
If(loggedIn,
NavbarItem("Log out", "/logout", false),
),
),
)
}
func NavbarItem(name, path string, active bool) Node {
return Li(A(Href(path), Text(name)), Classes{
"navbar-item": true,
"active": active,
"inactive": !active,
})
}
Miss those <tags>
or need to inject HTML from somewhere else? Use Raw
.
import (
. "maragu.dev/gomponents"
. "maragu.dev/gomponents/components"
. "maragu.dev/gomponents/html"
)
type NavLink struct {
Name string
Path string
}
func Navbar(loggedIn bool, links []NavLink, currentPath string) Node {
return Nav(Class("navbar"),
Raw(`<span class="logo"><img src="logo.png"></span>`),
Ol(
Map(links, func(l NavLink) Node {
return NavbarItem(l.Name, l.Path, l.Path == currentPath)
}),
If(loggedIn,
NavbarItem("Log out", "/logout", false),
),
),
)
}
func NavbarItem(name, path string, active bool) Node {
return Li(A(Href(path), Text(name)), Classes{
"navbar-item": true,
"active": active,
"inactive": !active,
})
}
Your editor helps you with auto-completion. It's type-safe. Nice formatting using gofmt. You can even use the debugger. And all common HTML elements and attributes are included!
$ go get maragu.dev/gomponents
There’s an example app inside the gomponents repository. It’s a simple web server that serves two HTML pages using gomponents and TailwindCSS.
There’s also the gomponents starter kit, a full template repository for building a web app with gomponents, TailwindCSS, and HTMX.
Piotr Kowalski created an online HTML-to-gomponents converter tool!
See also the Github repository or the blog post that started it all.