gsxui

Getting Started

gsxui components are copy-in: the CLI vendors real .gsx source into your own module, so what you build against is code you own and can edit — not a package you import and can't touch.

1. Install the CLI

go install github.com/gsxhq/gsxui/cmd/gsxui@latest

2. Initialize your project

In your project (a Go module):

gsxui init
gsxui initialized.
  css:  web/gsxui.css (import it from your entry point)
  js:   web/gsxui/index.js (import it from your entry point)
  next: gsxui add button

This vendors the theme tokens (web/gsxui.css), the JS runtime and behavior barrel (web/gsxui/), and the class merger (ui/merge/merge.go), then points gsx.toml'sclass_merger at it — the seam that makes caller-class-merge work (see Theming). It alsogo gets gsx and tailwind-merge-go, and installs the gsx tool via go get -tool.

web/gsxui.css begins with@import "tailwindcss" and@import "tw-animate-css" — your Tailwind build resolves both from npm, so make sure they're installed:npm install tailwindcss @tailwindcss/vite tw-animate-css. Without tw-animate-css everyanimate-in/animate-out class the components carry (dialog, dropdown, tooltip) is silently inert.

3. Add components

gsxui add button card
adding: button card
done — build with: go build ./...

card has no dependencies of its own, but a component that does (e.g. select, which needs icon) pulls its dependency in automatically — gsxui add select vendorsicon too. You own every file this writes:gsxui add never touches one you've already modified unless you pass --overwrite. After upgrading the gsxui binary, re-run gsxui add <name> --overwrite to refresh vendored components — that discards local edits to those files.

4. Your first page

A tiny two-file app: home.gsx renders a Card around aButton, and main.go serves it.

package main

import "example.com/myapp/ui"

component Home() {
	<html lang="en">
		<head>
			<meta charset="UTF-8"/>
			<link rel="stylesheet" href="/web/gsxui.css"/>
		</head>
		<body class="flex min-h-svh items-center justify-center bg-background p-8 text-foreground">
			<ui.Card class="w-full max-w-sm">
				<ui.CardHeader>
					<ui.CardTitle>Hello, gsxui</ui.CardTitle>
					<ui.CardDescription>Your first page.</ui.CardDescription>
				</ui.CardHeader>
				<ui.CardContent>
					<ui.Button>Click me</ui.Button>
				</ui.CardContent>
			</ui.Card>
		</body>
	</html>
}
package main

import (
	"log"
	"net/http"
)

func main() {
	http.Handle("/web/", http.StripPrefix("/web/", http.FileServer(http.Dir("web"))))
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		Home().Render(r.Context(), w)
	})
	log.Println("listening on http://localhost:8080")
	log.Fatal(http.ListenAndServe(":8080", nil))
}

Compile the .gsx file to plain Go, then run it:

go tool gsx generate

(silent on success — it writes home.x.go next tohome.gsx and exits 0)

go run .
2026/07/23 09:00:00 listening on http://localhost:8080

Open http://localhost:8080 — a styled Card with a Button inside, rendered with gsxui's default light theme. Next:restyle it.