Quick Start
A minimal structpages app with two pages and gsx-rendered HTML. Run it in under five minutes. (templ and html/template work too; see Using templ.)
1. Create the project
You need Go 1.26 or later.
bash
mkdir hello-structpages && cd hello-structpages
go mod init hello-structpages
go get github.com/jackielii/structpages
go get github.com/gsxhq/gsxPin the gsx CLI as a tool dep so collaborators get the right version:
bash
go get -tool github.com/gsxhq/gsx/cmd/gsx@latestThen register structpages' URL and id helpers as gsx filters. Create gsx.toml at the module root (gsx uses the nearest one walking up from the package):
toml
[filters]
url = "github.com/jackielii/structpages.URLFor"
id = "github.com/jackielii/structpages.ID"
target = "github.com/jackielii/structpages.IDTarget"2. Define your pages
Create pages.gsx:
gsx
package main
type pages struct {
home `route:"/{$} Home"`
about `route:"/about About"`
}
type home struct{}
type about struct{}
component (p home) Page() {
<html>
<body>
<h1>Hello, structpages!</h1>
<p><a href={about{} |> url}>About</a></p>
</body>
</html>
}
component (p about) Page() {
<html>
<body>
<h1>About</h1>
<p><a href={home{} |> url}>Home</a></p>
</body>
</html>
}Two things to note:
/{$}means "exactly/". A bareroute:"/"would be a ServeMux catch-all prefix that swallows every unmatched path;/{$}matches only the root.- Links use the
urlfilter, not string literals.{about{} |> url}isstructpages.URLFor(ctx, about{}); gsx passes the renderctxand returns any error fromRender. When a route moves, every link follows — andstructpages-lintflags any hard-coded internal path you write by accident.
3. Wire up main.go
go
package main
import (
"log"
"net/http"
"github.com/jackielii/structpages"
)
func main() {
mux := http.NewServeMux()
if _, err := structpages.Mount(mux, pages{}, "/", "Site"); err != nil {
log.Fatal(err)
}
log.Println("Listening on :8080")
log.Fatal(http.ListenAndServe(":8080", mux))
}4. Generate and run
bash
go tool gsx generate .
go mod tidy
go run .gsx generate writes pages.x.go next to pages.gsx; rerun it after editing .gsx files.
Open http://localhost:8080. You should see "Hello, structpages!" and a link to /about.
What just happened
pagesis a page group — a struct whose fields each carry a route tag, with no rendering of its own. Embedding means promoted methods, but the dispatcher skips promoted methods, so thePage()defined on each inner type is the one that runs.component (p home) Page()is a gsx method component:gsx generateturns it into a Go methodfunc (p home) Page() gsx.Node, and agsx.Nodeis a component (it hasRender(ctx, w)).Mount(mux, pages{}, "/", "Site")walks the struct and registers each route onmux.- Each request is dispatched to the matching page, which renders its Page method (a gsx component) into the response.
Next steps
- Concepts for the vocabulary the rest of these docs use.
- Routing for the full tag syntax (HTTP methods, path params, titles).
- gsx Patterns for shared layouts and the Props pattern.
- How a Request Is Rendered for what renders, and when Props overrides it.
- HTMX Integration for partial rendering driven by
hx-target(opt in withWithTargetSelector). - Testing for rendering pages and components in unit tests.
- Examples for full working apps in
examples/you can clone.