Guide for building server-rendered interactive Go UIs with g-sui. String-based components, server actions, WebSocket patches—no frontend framework needed.
Expert guidance for working with **g-sui**, a server-rendered UI framework for Go that enables building interactive web applications without client-side JavaScript frameworks.
g-sui is a Go framework where all HTML generation, business logic, and state management occur on the server. Interactivity is achieved through:
**1. Callable Type**: All handlers have signature `func(*ui.Context) string`
**2. Targets & Actions**:
```go
target := ui.Target() // Unique ID for DOM element
ctx.Call(handler).Render(target) // Replace innerHTML
ctx.Call(handler).Replace(target) // Replace entire element
ctx.Call(handler).Append(target) // Append to element
ctx.Call(handler).Prepend(target) // Prepend to element
ctx.Call(handler).None() // Fire-and-forget
```
**3. Context**: Request-scoped context carrying request, response writer, and state
```go
package main
import (
"embed"
"time"
"github.com/michalCapo/g-sui/ui"
)
//go:embed assets/*
var embedFS embed.FS
func main() {
app := ui.MakeApp("en") // Create app with locale
app.Page("/", HomePage) // Register page route
app.Page("/about", AboutPage)
app.Favicon(embedFS, "assets/favicon.ico", 24*time.Hour)
app.Assets(embedFS, "assets/", 24*time.Hour)
app.AutoRestart(true) // Dev: rebuild on file changes
app.PWA(ui.PWAConfig{ // Optional: Enable PWA
Name: "My App",
ShortName: "App",
})
app.Listen(":8080") // Start server + WebSocket
}
```
```go
func HomePage(ctx *ui.Context) string {
target := ui.Target()
return ui.Html(
ui.Head(
ui.Title("Home"),
ui.Meta("viewport", "width=device-width, initial-scale=1"),
ui.TailwindCDN(), // Load Tailwind CSS
),
ui.Body(
ui.Div().ID(target).Class("p-4").Render(
ui.H1("Welcome to g-sui"),
ui.Button().Click(
ctx.Call(Increment).Render(target),
).Render("Click Me"),
),
),
)
}
```
```go
type Counter struct {
Count int
}
func (c *Counter) Increment(ctx *ui.Context) string {
ctx.Body(c) // Restore state from request
c.Count++
return ui.Div().Render(
ui.P(ui.Sprintf("Count: %d", c.Count)),
ui.Button().Click(
ctx.Call(c.Increment, c).Render(ui.Target()),
).Render("Increment"),
)
}
```
```go
type LoginForm struct {
Email string `validate:"required,email"`
Password string `validate:"required,min=8"`
}
func LoginPage(ctx *ui.Context) string {
form := &LoginForm{}
target := ui.Target()
return ui.Html(
ui.Body(
ui.Div().ID(target).Render(
ui.Form().Submit(ctx.Submit(HandleLogin, form).Replace(target)).Render(
ui.Input("email").Type("email").Name("Email"),
ui.Input("password").Type("password").Name("Password"),
ui.Button().Type("submit").Render("Login"),
),
),
),
)
}
func HandleLogin(ctx *ui.Context) string {
form := &LoginForm{}
valid, errors := ctx.Validate(form)
if !valid {
return ui.Alert().Red().Render(ui.Sprintf("Errors: %v", errors))
}
// Process login...
return ui.Alert().Green().Render("Login successful!")
}
```
```go
type User struct {
ID int
Name string
Email string
}
func UsersPage(ctx *ui.Context) string {
collate := ui.Collate(func(query *ui.TQuery) []User {
// Fetch users from database with query params
return db.GetUsers(query)
})
collate.Search("Name", "Email")
collate.Sort("Name")
collate.Filter("Email")
collate.Excel("Name", "Email")
collate.Row(func(user *User, index int) string {
return ui.Sprintf("<tr><td>%s</td><td>%s</td></tr>", user.Name, user.Email)
})
return collate.Render(ctx, db)
}
```
```go
// Server-side: Push updates to clients
func BroadcastUpdate(ctx *ui.Context, target string) {
html := ui.Div().Render("Updated content")
ctx.WebSocket.Broadcast(target, html, "render")
}
```
```bash
go run examples/main.go # Run example app
go build # Build project
go test ./... # Run all tests
go test ./ui/... # Test specific package
go mod tidy # Tidy dependencies
./deploy # Create & push version tag
```
1. **XSS Protection**: All attributes escaped via `escapeAttr()`; JavaScript via `escapeJS()`
2. **CSP Headers**: Use `ctx.SetDefaultCSP()` or `ctx.SetCSP(policy)`
3. **Validation**: Leverage `go-playground/validator` for form validation
4. **Safe Methods**: Use `table.Head()` for escaped text, `table.HeadHTML()` for raw HTML
The project includes specialized skills in `docs/skills/`:
```bash
mkdir -p ~/.claude/skills/g-sui && curl -sL https://raw.githubusercontent.com/michalCapo/g-sui/main/docs/skills/SKILL.md -o ~/.claude/skills/g-sui/SKILL.md && curl -sL https://raw.githubusercontent.com/michalCapo/g-sui/main/docs/skills/CORE.md -o ~/.claude/skills/g-sui/CORE.md && curl -sL https://raw.githubusercontent.com/michalCapo/g-sui/main/docs/skills/COMPONENTS.md -o ~/.claude/skills/g-sui/COMPONENTS.md && curl -sL https://raw.githubusercontent.com/michalCapo/g-sui/main/docs/skills/DATA.md -o ~/.claude/skills/g-sui/DATA.md && curl -sL https://raw.githubusercontent.com/michalCapo/g-sui/main/docs/skills/SERVER.md -o ~/.claude/skills/g-sui/SERVER.md && curl -sL https://raw.githubusercontent.com/michalCapo/g-sui/main/docs/skills/PATTERNS.md -o ~/.claude/skills/g-sui/PATTERNS.md
```
Pass state through payload structs:
```go
ctx.Call(handler, &state).Render(target)
```
Use `ctx.Call(handler).Replace(target)` to swap entire modal content
Use `ctx.Call(loadMore, offset).Append(target)` for progressive loading
Use `ctx.Submit(handler).Replace(target)` for form handling with validation
**Best for:**
**Not ideal for:**
1. **Use Tailwind CSS** for rapid styling (included via CDN)
2. **Leverage data collation** for tables with search/sort/filter
3. **Test handlers** as pure functions returning HTML strings
4. **Use WebSocket** sparingly—prefer action-based updates
5. **Keep state minimal**—pass only what's needed in actions
6. **Validate early**—use `go-playground/validator` tags
7. **Escape HTML**—use safe methods like `table.Head()`
Leave a review
No reviews yet. Be the first to review this skill!
# Download SKILL.md from killerskills.ai/api/skills/g-sui-framework-expert/raw