Tree & content
These build a site rather than run in one, so they're real code rather than a hydrated widget.
Building the tree
ssg.Node and Build are the entire mechanism — this is what generates this site. See The Node tree.
go
root := ssg.Node{
Title: "My Site",
Page: ssg.Templ(views.Home),
Children: map[string]ssg.Node{
"about": {Title: "About", Page: ssg.Templ(views.About)},
"style": {CopyFrom: "style"},
"scripts": {CompileFrom: "scripts"},
},
}
if err := ssg.Build(root, ssg.BuildOptions{
RelativeURLs: true, // so the same build works under a subpath
BaseURL: "/my-site",
}); err != nil {
log.Fatal(err)
}Why relative URLs
RelativeURLs makes one build work opened from disk, at a localhost root, and under a subpath — BaseURL only feeds Ctx.URL, for canonical tags. See Ctx & URLs.
Markdown collections
content.LoadDir plus Node.Generate turns a directory of markdown into one index page and one page per file, with no path arithmetic. See Content & collections.
go
func projects(dir string) ssg.Node {
pages, err := content.LoadDir(dir)
if err != nil {
log.Fatal(err)
}
var tiles []templ.Component
children := map[string]ssg.Node{}
// Slugs, not a map range: map order would reshuffle the index every build.
for _, slug := range content.Slugs(pages) {
fm, _ := content.Decode[FrontMatter](pages[slug])
body := pages[slug].HTML
children[slug] = ssg.Node{
Title: fm.Title,
Page: func(c ssg.Ctx) templ.Component {
return views.ProjectPage(c.Title, c.Path, c.Prefix, body)
},
}
tiles = append(tiles, views.Tile(fm.Title, fm.Description, slug))
}
return ssg.Node{
Title: "Projects",
Page: func(c ssg.Ctx) templ.Component { return views.ProjectIndex(c.Title, c.Path, c.Prefix, tiles) },
Children: children,
}
}
// "projects": projects("content/projects"),Locales
A locale's subtree is derived from the base tree — you list only the pages that genuinely differ, and everything else is mirrored with the prefix set. See Locales & i18n.
go
root = ssg.BuildLocales(root, []ssg.Locale{{
Code: "de",
Prefix: "/de", // defaults to "/" + Code
Override: map[string]func(ssg.Node) ssg.Node{
"": func(n ssg.Node) ssg.Node {
n.Title, n.Page = "Meine Seite", ssg.Templ(views_de.Home)
return n
},
"about": func(n ssg.Node) ssg.Node {
n.Title = "Über mich" // Page and Children survive untouched
return n
},
},
}})
// An override is handed the node BuildLocales would otherwise have derived, so
// changing one field is one line. Everything not named — goals, links, and any
// generated collection children — appears under /de/ rendered by the same
// components, with Ctx.Prefix set to "/de".