templ-components
Reusable UI components for Go web apps — built on templ, HTMX, and Tailwind CSS.

No DaisyUI. No Node.js. No framework lock-in.
templ-components is a pure Tailwind CSS v4 component library for Go's templ engine, with first-class HTMX integration. Every component renders server-side, ships zero JavaScript by default, and uses only Tailwind utility classes — so you stay in full control of your CSS and build pipeline.
Status: Work in progress. No stability is guaranteed — APIs may change without notice at any time.
Why templ-components?
| Feature |
templ-components |
templUI |
goshipit |
| CSS approach |
Tailwind v4+ (CSS-first) |
Tailwind + CSS vars |
Tailwind + DaisyUI |
| JavaScript |
None (pure server-rendered) |
Alpine.js |
DaisyUI JS |
| Requires Node.js |
No |
No |
Yes |
| Go module |
Yes |
Yes |
Yes |
| Components |
73 |
40+ |
— |
| Dark mode |
Built-in (Tailwind dark:) |
CSS custom properties |
Via DaisyUI |
| CSP compliant |
Yes (nonce support) |
Yes |
— |
| Typed props |
26 string enums |
— |
— |
| HTMX integration |
Built-in package |
— |
— |
templ-components is for developers who want server-rendered HTML with zero client-side JavaScript — no Alpine.js, no DaisyUI, no build pipeline beyond templ generate.
Quick Start
1. Install
go get github.com/larsartmann/templ-components
2. Build a page
package main
import (
"github.com/larsartmann/templ-components/layout"
"github.com/larsartmann/templ-components/feedback"
"github.com/larsartmann/templ-components/display"
)
templ Page() {
@layout.Base(layout.DefaultPageProps()) {
@layout.ThemeScript("")
@feedback.ToastContainer("")
@display.Card(display.DefaultCardProps()) {
<h1>Hello, world</h1>
}
}
}
3. Generate and run
templ generate && go run .
Component Catalog
utils — Shared Types & Helpers
All component props embed utils.BaseProps for consistent ID, Class, Attrs, AriaLabel, and Nonce propagation. Key helpers:
utils.Class("bg-white dark:bg-gray-800", props.Class) // Tailwind class merging
utils.Ternary(condition, "yes", "no") // Generic ternary
layout — Page Structure
Base HTML layouts, theme toggle, and dark mode support.
@layout.Base(layout.DefaultPageProps()) { <main>Content</main> }
@layout.Minimal(layout.DefaultMinimalProps()) { <p>Static content</p> }
@layout.ThemeScript("")
@layout.ThemeToggle("Toggle theme", "")
display — Data Display (19 components)
Cards, badges, modals, tables, tabs, avatars, tooltips, accordions, dropdowns, stat cards, page headers, definition lists, and more.
@display.Card(display.CardProps{Title: "Users", Subtitle: "Manage users"}) {
<p>Card content</p>
}
@display.StatCard(display.StatCardProps{Label: "Users", Value: "1,204", Icon: icons.Users, Change: "12%", Trend: display.TrendUp})
@display.Badge(display.BadgeProps{Text: "Active", Type: display.BadgeSuccess, Dot: true})
@display.StatusBadge("healthy")
@display.Modal(display.ModalProps{Title: "Confirm", Size: display.ModalSizeSM}) {
<p>Are you sure?</p>
}
@display.Table(display.TableProps{
Headers: []string{"Name", "Email", "Role"},
Rows: []display.TableRow{
display.SimpleTableRow("Alice", "alice@example.com", "Admin"),
display.SimpleTableRow("Bob", "bob@example.com", "User"),
},
Striped: true,
})
@display.Tabs(display.TabsProps{
Tabs: []display.Tab{
{ID: "users", Label: "Users", Active: true},
{ID: "settings", Label: "Settings"},
},
})
@display.Accordion(display.AccordionProps{
Items: []display.AccordionItem{
{ID: "faq1", Title: "What is this?", Open: true},
{ID: "faq2", Title: "How does it work?"},
},
})
@display.Avatar(display.AvatarProps{Src: "/avatar.jpg", Alt: "Alice"})
@display.Tooltip(display.TooltipProps{Text: "More info"}) { <button>Hover me</button> }
@display.Dropdown(display.DropdownProps{Label: "Actions", Items: []display.DropdownItem{
{Text: "Edit", Href: "/edit"},
{Text: "Delete", Href: "/delete"},
}})
@display.PageHeader(display.PageHeaderProps{Title: "Users", Subtitle: "Manage accounts"})
@display.DefinitionList(display.DefinitionListProps{Items: []display.DefinitionItem{
{Term: "Email", Detail: "alice@example.com"},
{Term: "Status", Detail: "Active"},
}})
@display.ListNote(display.ListNoteProps{Shown: 50, Total: 127})
feedback — User Feedback (12 components)
Alerts, toasts, spinners, progress bars, skeletons, and loading states.
@feedback.ToastContainer("")
@feedback.Toast(feedback.ToastProps{Message: "Saved!", Type: feedback.ToastSuccess})
@feedback.Alert(feedback.AlertProps{
Title: "Warning", Message: "This cannot be undone.", Type: feedback.AlertWarning,
})
@feedback.Spinner(feedback.SpinnerMD, "text-blue-600")
@feedback.Skeleton(feedback.SkeletonCard)
@feedback.ProgressBar(feedback.ProgressBarProps{Current: 45, Total: 100})
@feedback.StepIndicator(feedback.StepIndicatorProps{
Steps: []string{"Details", "Review", "Confirm"}, CurrentStep: 1,
})
Inputs, selects, textareas, checkboxes, radios, toggles, file inputs, date pickers, comboboxes, labels, and validation errors.
@forms.Input(forms.InputProps{Name: "email", Type: forms.InputEmail, Label: "Email address"})
@forms.Textarea(forms.TextareaProps{Name: "bio", Label: "Bio", Rows: 4})
@forms.Select(forms.SelectProps{
Name: "country", Label: "Country",
Options: []forms.SelectOption{{Value: "de", Label: "Germany"}, {Value: "at", Label: "Austria"}},
})
@forms.Checkbox(forms.CheckboxProps{Name: "terms", Label: "I agree"})
@forms.RadioGroup(forms.RadioGroupProps{Name: "plan", Options: []forms.RadioOption{
{Value: "free", Label: "Free"}, {Value: "pro", Label: "Pro"},
}})
@forms.Toggle(forms.ToggleProps{Name: "notifications", Label: "Enable notifications"})
@forms.FileInput(forms.FileInputProps{Name: "avatar", Label: "Upload photo", Accept: "image/*"})
@forms.DatePicker(forms.DatePickerProps{Name: "dob", Label: "Date of birth"})
@forms.Combobox(forms.ComboboxProps{Name: "country", Label: "Country", Options: []forms.ComboboxOption{
{Value: "de", Label: "Germany"}, {Value: "at", Label: "Austria"},
}})
navigation — Navigation (10 components)
Nav bars, breadcrumbs, pagination, mobile menus, and sidebar navigation.
@navigation.SimpleNav(navigation.SimpleNavProps{
BrandText: "MyApp", BrandHref: "/",
Links: []navigation.NavLinkProps{
{Href: "/", Text: "Home"}, {Href: "/about", Text: "About"},
},
CurrentPath: "/",
})
@navigation.Breadcrumbs(navigation.BreadcrumbsProps{
Items: []navigation.BreadcrumbItem{
{Text: "Home", Href: "/"}, {Text: "Users", Active: true},
},
})
@navigation.Pagination(navigation.PaginationProps{CurrentPage: 2, TotalPages: 10, BaseURL: "/users"})
@navigation.Footer("MyApp")
@navigation.SidebarNav(navigation.SidebarNavProps{
Brand: templ.Raw(`<span class="font-bold text-white">MyApp</span>`),
Items: []navigation.SidebarNavItem{
{Label: "Dashboard", Href: "/", Icon: icons.Squares2x2},
{Label: "Users", Href: "/users", Icon: icons.Users},
},
CurrentPath: "/users",
})
icons — SVG Icons (101 icons)
Typed icon constants, no icon library dependency.
@icons.Icon(icons.Home, "h-5 w-5 text-gray-500")
@icons.Icon(icons.Check, "h-6 w-6 text-green-500")
101 named icons (100 path icons + Spinner) covering navigation, UI actions, communication, media, status, and admin/security. See icons/icon_names.go for the full list.
htmx — HTMX Integration (7 components)
Loading indicators, error handling, CSRF protection, and out-of-band swaps.
@htmx.GlobalErrorHandling(htmx.DefaultErrorHandlingConfig())
@htmx.LoadingIndicator(feedback.Spinner(feedback.SpinnerMD, "text-blue-600"))
@htmx.InlineLoadingOverlay("form-loading", feedback.Spinner(feedback.SpinnerSM, "text-white"))
@htmx.CSRFToken("token-value")
Design Principles
Type-safe. 26 typed string enums make invalid states unrepresentable. Props structs embed utils.BaseProps for consistent ID, class, attributes, ARIA label, and CSP nonce propagation.
Accessible. ARIA attributes, roles, keyboard navigation (modal focus trap, dropdown arrows, tabs), and screen-reader text across all interactive components.
CSP-ready. All inline scripts use nonce attributes. No eval(), no inline event handlers.
Dark mode. Every component supports Tailwind's dark: variant. Include @layout.ThemeScript("") to prevent flash of unstyled content.
Server-rendered. Zero client-side JavaScript by default. Interactive features (accordion, dropdown, modal, theme toggle) use minimal vanilla JS with nonce-based CSP.
Pay for what you use. Import only the packages you need. No monolithic bundle.
import (
"github.com/larsartmann/templ-components/display" // only if you use display components
"github.com/larsartmann/templ-components/feedback" // only if you use feedback components
)
Tailwind CSS Setup
Tailwind v4 uses CSS-first configuration. Since this is a Go module (not an npm package), you need to vendor the dependency so Tailwind can scan the .templ source files for class names:
go mod vendor
Then in your CSS:
/* app.css */
@import "tailwindcss";
/* Scan templ-components for class names */
@source "../vendor/github.com/larsartmann/templ-components";
/* Enable class-based dark mode toggle */
@custom-variant dark (&:where(.dark, .dark *));
Tailwind extracts class names from any file content — including .templ files — so no additional configuration is needed beyond the @source path.
Theming
Components emit standard Tailwind utility classes (bg-blue-600, text-gray-900). To customize colors without touching component code, override the underlying CSS variables in your @theme block:
/* app.css */
@import "tailwindcss";
@source "../vendor/github.com/larsartmann/templ-components";
/* Override the primary brand color globally */
@theme {
--color-blue-600: #4f46e5; /* changes ALL bg-blue-600, text-blue-600, etc. */
--color-blue-500: #6366f1;
}
For a ready-made configuration with semantic aliases (bg-tc-primary, text-tc-danger, etc.), copy the included theme file into your project:
@import "./templ-components-theme.css";
Or grab it from the repo root and customize:
curl -O https://raw.githubusercontent.com/larsartmann/templ-components/master/templ-components-theme.css
The theme file provides @custom-variant dark (required for ThemeScript/ThemeToggle) and semantic tokens like --color-tc-primary, --color-tc-surface, --color-tc-danger that you can use in your own CSS or Tailwind arbitrary values.
Tailwind v4+ is the standard
All LarsArtmann projects use Tailwind CSS v4+ (latest). CSS-first config, no Node.js runtime, no DaisyUI. Small custom CSS only where Tailwind doesn't cover something. New to Tailwind v4? See the docs/tailwind-v4-adoption-guide.md for setup, theming, and migration from custom CSS design systems.
The icons package is the only CSS-agnostic package (pure SVG path data). See docs/icons-only-adoption.md.
By the Numbers
| Metric |
Value |
| Components |
73 |
| SVG icons |
101 |
| Typed enums |
26 |
| Packages |
11 |
| Tests |
451 functions + ~650 subtests |
| Coverage |
~75% (72–79% per package) |
| Dependencies |
3 (templ, tailwind-merge-go, go-error-family) |
Requirements
- Go 1.26+
- templ CLI (install)
- Tailwind CSS 4.x+
- HTMX 2.x (optional, for
htmx package)
errorpage — Error Pages (3 components)
Structured error pages with family-aware styling, HTTP handler integration, and pre-built constructors.
@errorpage.ErrorPage(errorpage.ErrorPageProps{Family: errorpage.FamilyNotFound, Why: "Page not found"})
@errorpage.ErrorDetail(errorpage.ErrorDetailProps{Family: errorpage.FamilyConflict, Why: "Conflict", Fix: "Refresh and retry"})
@errorpage.ErrorAlert(errorpage.ErrorAlertProps{Family: errorpage.FamilyTransient, Why: "Temporary issue"})
HTTP handler integration:
http.Handle("/error", errorpage.ErrorHandler(err, errorpage.ErrorHandlerConfig{HTMLShell: true}))
http.Error(w, "not found", http.StatusNotFound) // use errorpage.NotFound() instead
Ecosystem
This library is part of the GOTH stack (Go + Templ + HTMX):
| Project |
What it does |
| cqrs-htmx |
Production CQRS+ES framework with WebAuthn, RBAC, multi-tenancy, SSE. Uses templ-components in its admin panel. |
| go-cqrs-lite |
Minimal CQRS/ES building blocks (command bus, event store, projections, snapshots). |
| go-error-family |
Structured error families (classified, contextual, actionable). Used by templ-components' errorpage package. |
Contributing
Contributions are welcome. See CONTRIBUTING.md for setup, conventions, and workflow.
License
MIT