maragudk/gomponents

HTML components in pure Go.

Go

1,885

339 commits

updated Sep 30, 2026

See the code

README

Tired of complex template languages?

Logo

GoDoc CI codecov

Try HTML components in pure Go.

gomponents are HTML components written 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

Made with ✨sparkles✨ by maragu: independent software consulting for cloud-native Go apps & AI engineering.

Contact me at markus@maragu.dk for consulting work, or perhaps an invoice to support this project?

Features

Check out www.gomponents.com for an introduction or pkg.go.dev/maragu.dev/gomponents for the official docs.

  • Build reusable HTML components
  • Write declarative HTML 5 in Go without all the strings, so you get
    • Type safety from the compiler
    • Auto-completion from the IDE
    • Easy debugging with the standard Go debugger
    • Automatic formatting with gofmt/goimports
  • Simple API that's easy to learn and use (you know most already if you know HTML)
  • Useful helpers like
    • Text and Textf that insert HTML-escaped text,
    • Raw and Rawf for inserting raw strings,
    • Map for mapping data to components and Group for grouping components,
    • and If/Iff for conditional rendering.
  • No external dependencies
  • Mature and stable, no breaking changes

Usage

go get maragu.dev/gomponents
package main

import (
	. "maragu.dev/gomponents"
	. "maragu.dev/gomponents/components"
	. "maragu.dev/gomponents/html"
)

func Navbar(authenticated bool, currentPath string) Node {
	return Nav(
		NavbarLink("/", "Home", currentPath),
		NavbarLink("/about", "About", currentPath),
		If(authenticated, NavbarLink("/profile", "Profile", currentPath)),
	)
}

func NavbarLink(href, name, currentPath string) Node {
	return A(Href(href), Classes{"is-active": currentPath == href}, Text(name))
}

(Some people don't like dot-imports, and luckily it's completely optional.)

For a more complete example, see the examples directory. There's also the gomponents-starter-kit for a full application template.

Coding agents

There's a skill that teaches coding agents how to use gomponents. It's also a good, concise introduction to gomponents for humans. Install it as a plugin:

Claude Code
/plugin marketplace add maragudk/gomponents
/plugin install gomponents@gomponents
Codex

Run these commands from a local clone:

codex plugin marketplace add maragudk/gomponents
codex plugin add gomponents@gomponents

Architecture

gomponents is organized into several packages:

  • gomponents: Core interfaces and functions like Node, El, Attr, and helpers like Map, Group, If, Text, Raw.
  • gomponents/html: HTML elements and attributes.
  • gomponents/components: Higher-level components and utilities.
  • gomponents/http: HTTP-related utilities for web servers.
  • gomponents/x/...: Experimental packages. These do not have the same compatibility guarantees as the core library, and in particular, may get breaking changes.

Void Elements

Void elements in HTML (like <br>, <img>, <input>) don't have closing tags. gomponents handles these correctly by checking against an internal list of void elements during rendering. When you create a void element, any child nodes that are not attributes will be ignored automatically to ensure valid HTML output.

FAQ

Is gomponents production-ready?

Yes! gomponents is mature, stable, fully tested with 100% coverage, and is used in production by myself and many others, and has been for years.

Should I choose html/template, Templ, or gomponents?

These are all good choices, and it largely comes down to preference. I wrote gomponents because I didn't like how I think it's hard to pass data around between templates in html/template. gomponents is pure Go, with no extra build step like Templ, so it works with all tools that already support Go.

That said, both html/template and Templ will do the same thing as gomponents in the end. Try them all and choose what you like!

Is gomponents fast?

Yes. gomponents renders directly to an io.Writer, making it efficient for server-side rendering. The library avoids unnecessary memory allocations where possible. There's also an extensive benchmark suite to keep it that way, which you can run with make benchmark.

Some highlights from an Apple M4, constructing and rendering to io.Discard:

Benchmarkns/opB/opallocs/op
El16481
Attr, boolean10241
Attr, key-value18481
Text13161
Textf41402
Raw300
Rawf36402
Realistic full page635601265802858

I don't like how HTML looks in Go.

First of all, that's not a question. 😉

More seriously, think of gomponents like a DSL for HTML. You're building UI components. Give it a day, and it'll feel natural.

I'd like to add feature X, can I do that?

First of all, thank you for wanting to contribute! 😊 See CONTRIBUTING.md for a guide.

I accept code contributions, especially with new HTML elements and attributes. I always welcome issues discussing interesting aspects of gomponents, and obviously bug reports and the like. But otherwise, I consider gomponents pretty much feature complete.

New features to the core library are unlikely to be merged, since I like keeping it simple and the API small. In particular, new flow control functions (IfElse/Else) will not be added to the core library. For generic collection utilities like Map, Filter, and Reduce, see the experimental x/slices package.

If there's something missing that you need, I would recommend to keep small helper functions around in your own projects. And if all else fails, you can always use an IIFE:

func list(ordered bool) Node {
	return func() Node {
		// Do whatever you need to do, imperatively
		if ordered {
			return Ol()
		} else {
			return Ul()
		}
	}()
}

What's up with the specially named elements and attributes?

Unfortunately, there are some name clashes in HTML elements and attributes, so they need an El or Attr suffix, to be able to co-exist in the same package in Go.

I've chosen one or the other based on what I think is the common usage:

  • cite: Cite (element) / CiteAttr (attribute)
  • data: DataEl (element) / Data (attribute)
  • form: Form (element) / FormAttr (attribute)
  • label: Label (element) / LabelAttr (attribute)
  • style: StyleEl (element) / Style (attribute)
  • title: TitleEl (element) / Title (attribute)

Deprecated aliases (CiteEl, DataAttr, FormEl, LabelEl, StyleAttr, TitleAttr) also exist for backwards compatibility but should not be used in new code.

go
golang
gomponents
html

Significant stargazers

(top 24 of 28)

Trim21

238 followers · starred Apr 2025

qbit

387 followers · starred Aug 2024

Justin Clift

363 followers · starred Apr 2026

Alexander F. Rødseth

652 followers · starred May 2026

maragudk/gomponents

HTML components in pure Go.

Go

1,885

339 commits

updated Sep 30, 2026

See the code

README

Tired of complex template languages?

Logo

GoDoc CI codecov

Try HTML components in pure Go.

gomponents are HTML components written 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

Made with ✨sparkles✨ by maragu: independent software consulting for cloud-native Go apps & AI engineering.

Contact me at markus@maragu.dk for consulting work, or perhaps an invoice to support this project?

Features

Check out www.gomponents.com for an introduction or pkg.go.dev/maragu.dev/gomponents for the official docs.

  • Build reusable HTML components
  • Write declarative HTML 5 in Go without all the strings, so you get
    • Type safety from the compiler
    • Auto-completion from the IDE
    • Easy debugging with the standard Go debugger
    • Automatic formatting with gofmt/goimports
  • Simple API that's easy to learn and use (you know most already if you know HTML)
  • Useful helpers like
    • Text and Textf that insert HTML-escaped text,
    • Raw and Rawf for inserting raw strings,
    • Map for mapping data to components and Group for grouping components,
    • and If/Iff for conditional rendering.
  • No external dependencies
  • Mature and stable, no breaking changes

Usage

go get maragu.dev/gomponents
package main

import (
	. "maragu.dev/gomponents"
	. "maragu.dev/gomponents/components"
	. "maragu.dev/gomponents/html"
)

func Navbar(authenticated bool, currentPath string) Node {
	return Nav(
		NavbarLink("/", "Home", currentPath),
		NavbarLink("/about", "About", currentPath),
		If(authenticated, NavbarLink("/profile", "Profile", currentPath)),
	)
}

func NavbarLink(href, name, currentPath string) Node {
	return A(Href(href), Classes{"is-active": currentPath == href}, Text(name))
}

(Some people don't like dot-imports, and luckily it's completely optional.)

For a more complete example, see the examples directory. There's also the gomponents-starter-kit for a full application template.

Coding agents

There's a skill that teaches coding agents how to use gomponents. It's also a good, concise introduction to gomponents for humans. Install it as a plugin:

Claude Code
/plugin marketplace add maragudk/gomponents
/plugin install gomponents@gomponents
Codex

Run these commands from a local clone:

codex plugin marketplace add maragudk/gomponents
codex plugin add gomponents@gomponents

Architecture

gomponents is organized into several packages:

  • gomponents: Core interfaces and functions like Node, El, Attr, and helpers like Map, Group, If, Text, Raw.
  • gomponents/html: HTML elements and attributes.
  • gomponents/components: Higher-level components and utilities.
  • gomponents/http: HTTP-related utilities for web servers.
  • gomponents/x/...: Experimental packages. These do not have the same compatibility guarantees as the core library, and in particular, may get breaking changes.

Void Elements

Void elements in HTML (like <br>, <img>, <input>) don't have closing tags. gomponents handles these correctly by checking against an internal list of void elements during rendering. When you create a void element, any child nodes that are not attributes will be ignored automatically to ensure valid HTML output.

FAQ

Is gomponents production-ready?

Yes! gomponents is mature, stable, fully tested with 100% coverage, and is used in production by myself and many others, and has been for years.

Should I choose html/template, Templ, or gomponents?

These are all good choices, and it largely comes down to preference. I wrote gomponents because I didn't like how I think it's hard to pass data around between templates in html/template. gomponents is pure Go, with no extra build step like Templ, so it works with all tools that already support Go.

That said, both html/template and Templ will do the same thing as gomponents in the end. Try them all and choose what you like!

Is gomponents fast?

Yes. gomponents renders directly to an io.Writer, making it efficient for server-side rendering. The library avoids unnecessary memory allocations where possible. There's also an extensive benchmark suite to keep it that way, which you can run with make benchmark.

Some highlights from an Apple M4, constructing and rendering to io.Discard:

Benchmarkns/opB/opallocs/op
El16481
Attr, boolean10241
Attr, key-value18481
Text13161
Textf41402
Raw300
Rawf36402
Realistic full page635601265802858

I don't like how HTML looks in Go.

First of all, that's not a question. 😉

More seriously, think of gomponents like a DSL for HTML. You're building UI components. Give it a day, and it'll feel natural.

I'd like to add feature X, can I do that?

First of all, thank you for wanting to contribute! 😊 See CONTRIBUTING.md for a guide.

I accept code contributions, especially with new HTML elements and attributes. I always welcome issues discussing interesting aspects of gomponents, and obviously bug reports and the like. But otherwise, I consider gomponents pretty much feature complete.

New features to the core library are unlikely to be merged, since I like keeping it simple and the API small. In particular, new flow control functions (IfElse/Else) will not be added to the core library. For generic collection utilities like Map, Filter, and Reduce, see the experimental x/slices package.

If there's something missing that you need, I would recommend to keep small helper functions around in your own projects. And if all else fails, you can always use an IIFE:

func list(ordered bool) Node {
	return func() Node {
		// Do whatever you need to do, imperatively
		if ordered {
			return Ol()
		} else {
			return Ul()
		}
	}()
}

What's up with the specially named elements and attributes?

Unfortunately, there are some name clashes in HTML elements and attributes, so they need an El or Attr suffix, to be able to co-exist in the same package in Go.

I've chosen one or the other based on what I think is the common usage:

  • cite: Cite (element) / CiteAttr (attribute)
  • data: DataEl (element) / Data (attribute)
  • form: Form (element) / FormAttr (attribute)
  • label: Label (element) / LabelAttr (attribute)
  • style: StyleEl (element) / Style (attribute)
  • title: TitleEl (element) / Title (attribute)

Deprecated aliases (CiteEl, DataAttr, FormEl, LabelEl, StyleAttr, TitleAttr) also exist for backwards compatibility but should not be used in new code.

go
golang
gomponents
html

Significant stargazers

(top 24 of 28)

Trim21

238 followers · starred Apr 2025

qbit

387 followers · starred Aug 2024

Justin Clift

363 followers · starred Apr 2026

Alexander F. Rødseth

652 followers · starred May 2026

Languages

Go

99.4%