Skip to content

Control flow

gsx control-flow blocks embed standard Go if, for, and switch statements directly into a component body. The outer { … } tells the parser that the content is dynamic — a Go control-flow construct whose branches contribute markup — as opposed to a literal HTML element or an interpolated value.

Control flow is real Go: the branches are ordinary Go branches, the loop variable is an ordinary Go variable, and the generated code is straightforward Go with markup-writing calls inserted where elements appear. There is no new template language to learn.

If / else

{ if cond { <markup> } } conditionally renders its child markup. An else branch renders when the condition is false.

gsx
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>
}

Renders:

html
<section><h1>Hi World</h1><p class="badge">2 new</p></section>

▶ Open in Playground

Both branches can contain any markup — elements, interpolations, nested components, even further control-flow blocks. The else clause is optional; omitting it means no output is produced when the condition is false.

{ if cond { … } } is distinct from {{ stmt }} (a GoBlock): the GoBlock runs a Go statement and produces no HTML output. The control-flow form here produces markup from whichever branch is taken. See Raw Go for GoBlocks.

For / range

{ for … := range … { <markup> } } renders its child markup once per element. The loop variable is in scope inside the block.

gsx
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>
}

Renders:

html
<ul><li>alpha: 1</li><li>beta: 2</li></ul>

▶ Open in Playground

The loop is a regular Go range loop. Both index and value variables follow normal Go scoping: declare them with := inside the loop header, and they are only available inside the loop body. A blank identifier _ discards the index when only the value is needed.

Any Go iterable is supported: slices, arrays, maps, strings (rune iteration), channels, and — with Go 1.23 — range-over functions. The loop body can contain any markup.

Switch

{ switch expr { case x: <markup> default: <markup> } } selects one branch of markup. The case and default labels are not surrounded by braces; the markup they contain is their body.

gsx
package views

component Badge(kind string) {
	<span>
		{ switch kind {
			case "warn":
				<b>warning</b>
			case "err":
				<b>error</b>
			default:
				<b>info</b>
		} }
	</span>
}

Renders:

html
<span><b>warning</b></span>

▶ Open in Playground

Each case label can list multiple comma-separated values, exactly as in Go. A default branch handles values that match no case. gsx lowers { switch … } to a native Go switch statement; as in Go, a matched case runs only its own branch — cases do not fall through implicitly.

Init statements

An if or for statement can include an init statement before the condition. The init statement runs first, and any variables it declares are scoped to the entire control block — both the condition and all branches.

gsx
package views

func loadUser(id string) (string, error) { return "User:" + id, nil }

component Profile(id string) {
	<div>
		{ if name, err := loadUser(id); err != nil {
			<span class="err">{ err.Error() }</span>
		} else {
			<span>{ name }</span>
		} }
	</div>
}

Renders:

html
<div><span>User:42</span></div>

▶ Open in Playground

The form { if v, err := f(); err != nil { … } else { … } } is the idiomatic way to handle a (T, error) return without auto-unwrapping. When you write { f() } as a bare interpolation and f returns (T, error), gsx auto-unwraps: it propagates the error to the caller. An init-statement if lets you inspect the error in-place and produce fallback markup instead of propagating.

The same init-statement form works with map lookups ({ if v, ok := m[k]; ok { … } }), type assertions, and any other Go expression that returns multiple values.