Examples
A gallery of gsx features. Each example is compiled and checked in CI; click Open in Playground to run and edit it live.
Basics
Interpolation & props
Components take a typed props struct; {expr} interpolates Go values, HTML-escaped.
package views
component Greeting(name string, count int) {
<p>Hello, { name }! You have { count } messages.</p>
}Attributes
Expression attributes (attr={expr}), boolean attributes, and a conditional attribute via { if … { attr=… } }.
package views
component Link(url string, label string, external bool, featured bool) {
<a
href={url}
data-count={3}
aria-current={external}
{ if featured {
class="featured"
} }
>
{ label }
</a>
}Auto-escaping & safe raw
User input is HTML-escaped by construction — no XSS. Use gsx.Raw / gsx.RawURL to opt out deliberately.
package views
// User input is HTML-escaped by construction — no XSS.
component Comment(body string) {
<blockquote>{ body }</blockquote>
}Control flow
If / else
Brace { if … else … } blocks contribute markup conditionally.
package views
component Inbox(name string, count int) {
<section>
<h1>Hi { name }</h1>
{ if count > 0 {
<p class="badge">{ count } new</p>
} else {
<p>all caught up</p>
} }
</section>
}Loops over lists
{ for … := range … } renders markup per element using a real Go range loop.
package views
type Item struct {
Name string
Count int
}
component List(items []Item) {
<ul>
{ for _, it := range items {
<li>{ it.Name }: { it.Count }</li>
} }
</ul>
}Switch
{ switch … } selects one branch of markup; default handles the rest.
package views
component Badge(kind string) {
<span>
{ switch kind {
case "warn":
<b>warning</b>
case "err":
<b>error</b>
default:
<b>info</b>
} }
</span>
}Components & composition
Components & props
Call a component with a typed props struct; boolean props pass bare (featured).
package views
component Card(title string, featured bool, count int) {
<div class={ "card", "card-featured": featured }>
<h2>{ title }</h2>
<span>{ count }</span>
</div>
}
component Page(t string, n int) {
<Card title={t} featured count={n}/>
}Children
A component renders its nested markup via {children} (like JSX children / a Vue default slot).
package views
component Card(title string) {
<article class="card">
<h3>{ title }</h3>
<div class="card__body">{ children }</div>
</article>
}
component Page() {
<Card title="Hello">
<em>composed</em>
</Card>
}Named slots
Pass markup into named gsx.Node props — header and footer slots.
package views
import "github.com/gsxhq/gsx"
component Panel(header gsx.Node, footer gsx.Node) {
<div class="panel">
<header>{ header }</header>
<footer>{ footer }</footer>
</div>
}
component Page() {
<Panel header={ <h1>H</h1> } footer="F"/>
}Template composition
A shared component library composed by a page method — multiple files, one render entry. This is the multi-file showcase.
components.gsx
package views
component Button(label string) {
<button class="btn">{ label }</button>
}
component Card(title string) {
<section class="card">
<h2>{ title }</h2>
{ children }
</section>
}page.gsx
package views
type HomePage struct {
Title string
}
component (p HomePage) Render() {
<main>
<Card title={p.Title}>
<Button label="Save"/>
</Card>
</main>
}Fallthrough attributes
Undeclared call-site attributes (class, data-, hx-) fall through to the component's root element; class merges (caller-wins).
package views
component Button(variant string) {
<button class="btn" data-variant={variant}>{ children }</button>
}
component Page() {
<Button variant="primary" class="w-full" data-test="x" hx-post="/go">
Save
</Button>
}Method components
Components can be methods on a type, so page state lives on the receiver (p) and per-call data in the props struct.
package views
type UsersPage struct {
Title string
Sort string
}
component (p UsersPage) Grid(sort string) {
<div>{ sort }-{ p.Title }</div>
}Styling
Composable class
The class attribute takes "always" entries and "name": cond toggles (like clsx / Vue :class).
package views
component Tag(label string, active bool) {
<span class={ "tag", "tag--active": active }>{ label }</span>
}Style blocks
A <style> block interpolates values with @{ … }; interpolated values are CSS-sanitized by construction.
package views
component Card(w int, userColor string) {
<style>
.card {
width: @{ w }px;
color: @{ userColor };
}
</style>
}Transforming values
Pipelines / filters
Transform values with typed filter pipelines — { x |> trim |> upper } — drawn from the gsx info registry.
package views
component Hi(name string) {
<p>{ name |> trim |> upper }</p>
}Interactive & whole-page
Fragments
A component can return multiple roots with no wrapper element using <>…</>.
package views
component Pair(a string, b string) {
<><span>{ a }</span><span>{ b }</span></>
}Forms
A reusable Field component forwards undeclared attributes ({ attrs... }) onto its input, so callers add type/name/required without Field declaring them.
package views
component Field(label string) {
<div class="field">
<label>{ label }</label>
<input class="control" { attrs... }/>
</div>
}
component LoginForm() {
<form method="post" action="/login">
<Field label="Email" type="email" name="email" required/>
<Field label="Password" type="password" name="password" required/>
<button type="submit">Sign in</button>
</form>
}JS attributes & data islands
@click={ gsx.RawJS(…) } emits a vouched event handler; a <script type="application/json"> island serializes typed Go data with @{ … } for client JS.
package views
import "github.com/gsxhq/gsx"
type Config struct {
Env string
Beta bool
}
component Widget(cfg Config) {
<div>
<button @click={gsx.RawJS("toggle()")}>Toggle</button>
<script type="application/json" id="cfg">@{ cfg }</script>
</div>
}Full HTML document
Render a whole page, including <!DOCTYPE html>, as one component.
package views
component Page(title string) {
<!DOCTYPE html>
<html lang="en">
<head>
<title>{ title }</title>
</head>
<body>hi</body>
</html>
}