Skip to content

Elements

Elements use familiar HTML syntax: attributes go in the opening tag and children nest between opening and closing tags. Dynamic values use Go expressions in braces.

Tags and nesting

gsx
package views

component Card(title string, body string) {
	<article class="card">
		<h2>{title}</h2>
		<p>{body}</p>
	</article>
}

Static attributes use quoted strings, while dynamic attributes use expressions such as disabled={disabled}. See Attributes for boolean attributes, spreads, and composition.

Whether a tag is an element or a component depends on its name and declarations in the package. See Basic Syntax — Element or component?.

Void elements

HTML void elements have no children. Write them with />; they render without a separate closing tag.

gsx
package views

component AvatarField(src string, alt string, disabled bool) {
	<div>
		<img src={src} alt={alt}/>
		<br/>
		<input type="text" required disabled={disabled}/>
	</div>
}

Renders:

html
<div><img src="/avatar.png" alt="User"><br><input type="text" required disabled></div>

▶ Open in Playground

Tag serialization

gsx generates canonical HTML tag shapes regardless of how you write them:

  • <div/> (any non-void element, including SVG) renders as <div></div> — browsers ignore the / on non-void elements and would treat <div/> as an unclosed open tag.
  • <br/> and <br></br> both render as <br>.
  • A void element with children (<br>text</br>) is a compile error.

Set serialization = "verbatim" in gsx.toml to emit authored shapes unchanged.

gsx
package views

component Divider() {
	<section>
		<div role="separator"/>
		<p>Content after the divider stays outside it.</p>
	</section>
}

Renders:

html
<section><div role="separator"></div><p>Content after the divider stays outside it.</p></section>

▶ Open in Playground

Raw-text elements

The bodies of <script> and <style> are literal text, so < is not treated as a nested tag. Use @{ expression } for dynamic values inside them.

gsx
package views

component Tracker() {
	<script>
		const threshold = 5;
		if (count < threshold) { track("below"); }
	</script>
}

Renders:

html
<script>const threshold = 5;
if (count < threshold) { track("below"); }</script>

▶ Open in Playground

See JavaScript and Styling for interpolation and minification options.

Full documents

A component can render a complete HTML document. Place <!DOCTYPE html> before the root <html> element.

gsx
package views

component Page(title string) {
	<!DOCTYPE html>
	<html lang="en">
		<head>
			<title>{ title }</title>
		</head>
		<body>hi</body>
	</html>
}

Renders:

html
<!DOCTYPE html><html lang="en"><head><title>Home</title></head><body>hi</body></html>

▶ Open in Playground

Elements as values

An element can be used wherever Go expects an expression in a .gsx file: a variable initializer, return value, function argument, struct field, slice item, map item, or interpolation operand. The result is a gsx.Node.

gsx
package views

var help = <a href="/help">Help</a>
var empty = <><h2>No results</h2><p>Try another search.</p></>

component Page() {
	<main>{help}{empty}</main>
}

A fragment (<>…</>) works in the same expression positions when the value needs multiple sibling roots. See Fragments.