shadcn-style copy-paste bricks for building Go interfaces and React SPAs from the same design contract. Props, variants, children, and composition work the way you expect — on both runtimes.
| React (Vite) | Go Templ (SSR) |
|---|---|
| ```tsx | ```templ |
| import { Button } from "@registry/ui"; | import "github.com/fastygo/templ/ui" |
| @ui.Button(ui.ButtonProps{ | |
| Save | Variant: "outline", |
| Size: "sm", | |
| }) { | |
| Save | |
| } | |
| ``` | ``` |
New here? Read docs/mental-model.md first — five ideas
that explain the whole registry. Coming from shadcn? See
docs/coming-from-shadcn.md. For a guided
split-view lesson, start at docs/learn/.
Every brick colocates its design contract:
ui/button/
button.spec.md # API, semantics, showcase
button.variants.json # shared variant recipe (both runtimes read this)
button.templ # Go Templ runtime
button.tsx # React runtime
button.variants.json is the single source of variant class strings. Both
button.templ and button.tsx compose classes from the same JSON.
Not a-h/templ. This repository is a separate component library for Go UI: copy-paste
.templbricks, helpers, and specs. It is built on top of Adrian Hacker's excellent templ project and uses the official code generatorgithub.com/a-h/templ/cmd/templ(viago tool templingo.mod). We are grateful for that foundation — templ's design and tooling make a typed, server-rendered component workflow practical.The module path is
github.com/fastygo/templso imports read naturally in application code — for exampleimport "github.com/fastygo/templ/ui"andimport cmp "github.com/fastygo/templ/components". That name reflects where these components live, not a claim to be the templ language or compiler. When you need templ itself, depend ongithub.com/a-h/templ; when you need this registry, depend ongithub.com/fastygo/templ.
| Path | Role |
|---|---|
utils/ |
Only shared Go helpers — CVA (Compose), Cn, attrs, ARIA, tags.go, form recipes |
ui/ |
Primitives: button, badge, input, form controls, layout, media, links, dialog, disclosure, card, alert, breadcrumb, icon badge |
components/ |
Behavior-driven composites: sheet, nav, tabs, popover, combobox, menu, toast |
components.json |
Registry manifest (shadcn-style) |
examples/ |
Optional local preview — templ/ (Go) and vite/ (React) share web/static/ |
.validate/docs/component.spec.template.md |
Spec template: api + showcase per brick |
.validate/docs/README.md |
Local validate-spec command and registry checks |
.cursor/rules/templ-component-spec.mdc |
Authoring rule for specs and STE comments |
.cursor/rules/ |
Agent rules — registry structure, ui8px, ARIA, examples, validation |
.ui8px/policy/ |
Tailwind class policy (ui/, components/, utils/, examples/) |
docs/ |
Long-lived architecture, ARIA, shadcn onboarding, publishing docs |
docs/publishing.md |
go get vs npm, export-ignore, release |
docs/architecture.md |
Dual-stack brick contract |
docs/aria.md |
@ui8kit/aria, opt-in behavior hooks, Sheet contract |
utils/utils.spec.md |
Helper module export catalog (classes, attrs, recipes) |
Default bricks avoid app-specific runtime dependencies. Behavior hooks such as data-ui8kit are opt-in per component, so each consuming app can choose its own client layer.
The migration guide is now documented in
docs/coming-from-shadcn.md.
Quick mapping:
cvarecipes -> colocated*.variants.jsonshared by Go + React.cn-> sameclsx + tailwind-mergebehavior.asChild-> Radix-styleSlotfor clickable bricks.Title as={n}-> preferH1...H6when heading level is static.- Card uses named exports only (
CardHeader,CardTitle, ...).
- Copy
utils/once (importgithub.com/fastygo/templ/utils, packageuiutils). - Copy folders from
ui/button/,ui/input/,ui/card/, etc. - Update the
uiutilsimport path in each.templfile to match your module. - Run
templ generateand includeui/**/*.templin your Tailwind@source. - Install peer deps in your app runtime:
react,react-dom,clsx, andtailwind-merge. - Define your own shadcn-compatible CSS variables (
--background,--primary, …) in the consuming app — the library does not ship tokens.
The repository is a Bun workspace root (examples is the only workspace
member today). A single bun install run from the repository root installs
both the registry devDependencies and the examples workspace in one shared
node_modules — no separate install step inside examples/.
bun install
bun run dev:vite # React preview at http://127.0.0.1:5173/
bun run dev:templ # Go Templ preview at http://127.0.0.1:8080/See examples/README.md for the full preview server and Tailwind setup.
Facade (recommended):
import "github.com/fastygo/templ/ui"
import cmp "github.com/fastygo/templ/components"
@ui.Button(ui.ButtonProps{Variant: "outline", Size: "sm"}) {
Save
}
@ui.Card(ui.CardProps{Variant: "default"}) {
…
}
@cmp.Sheet(cmp.SheetProps{ID: "panel", Side: "right", Behavior: "ui8kit"}) {
…
}Granular (optional):
import "your/module/ui/button"
@button.Button(button.ButtonProps{Variant: "outline", Size: "sm"}) {
Save
}- Variants:
ButtonVariants+ButtonClasses(props)— same mental model asbuttonVariants()in React. - className:
Classfield (additive; do not override size/variant utilities). - children:
{ children... }in the component body. - attrs:
Attrs templ.AttributesplusID,AriaLabel,Href, etc.
From the repository root:
go tool templ generate ./...Or per package, e.g. go tool templ generate ./ui/button.
Before CI, run the full registry check:
bun install
bun run verifyOr step by step:
bash .validate/scripts/validate-spec.sh --with-tests
bun run lint:ui8px
bun run validate:aria
go test ./...lint:ui8px covers ui/, components/, utils/, and examples/ against .ui8px/policy/.
See .validate/docs/README.md for spec checks and components.json alias expectations.
MIT — see LICENSE. Copyright (c) FastyGo.
This registry depends on a-h/templ; use and attribution for that project follow its own license and documentation.