astwerk

Ctx & URLs

What the walker tells a page — paths, locale, base — and how to build URLs from it.

Every page is a function of a Ctx describing where the page lives. URL building goes through Ctx too: Asset and Link are the only correct way to reference the site's own files. The tree these pages hang off is on The Node tree.

Documentation

Ctx

type Ctx struct {
	Title    string // the node's Title
	Path     string // "about/", "de/projects/thing/"
	Prefix   string // "" or "/de" — the locale's URL prefix
	Locale   string // "" or "de"
	Base     string // "" or "/astwerk" — the site's base path
	Relative bool   // RelativeURLs is on — Asset/Link emit relative URLs
}

func (c Ctx) URL() string               // this page's absolute URL
func (c Ctx) Asset(path string) string  // base-aware asset URL
func (c Ctx) Link(path string) string   // base- and locale-aware link
func (c Ctx) Rel() string               // Path without the locale segment
func (c Ctx) InLocale(prefix string) string // this page's URL in another locale

The walker already knows where a page lives, so it tells the page instead of making you retype it. Title comes from the node; Path, Prefix and Locale are filled in by the walker — see Locales & i18n for what the locale fields mean.

templ Layout(c ssg.Ctx) {
	<link rel="stylesheet" href={ c.Asset("style/style.css") }/>
	<a href={ c.Link("docs/") }>Docs</a>
}

Asset resolves a path relative to the site root; Link does the same and applies the locale prefix. Never write a URL as a literal: /style/style.css breaks the moment the site is served from a subdirectory.

Name the parameter c, not ctx: templ's generated code declares its own ctx, and a parameter with that name won't compile.

Why a literal breaks under a subdirectory

Every GitHub project Pages site is served from user.github.io/repo/ — a subdirectory of the domain. A literal /style/style.css resolves against the domain root, which is a different site, and there is no URL rewriting to save you. Asset and Link embed the base path (or emit relative URLs, below) and keep working wherever the output is served.

Relative URLs

For portable output, build with relative URLs:

ssg.Build(root, ssg.BuildOptions{
	RelativeURLs: true,
	BaseURL:      "/astwerk", // only feeds Ctx.URL, for canonical tags
})

One build then works opened from disk, at a localhost root, and deployed under /astwerk/ — no rebuild, no flag.

Why Ctx.URL stays absolute

Ctx.URL is unaffected by RelativeURLs and always carries the base path, because canonical links and Open Graph tags have to be absolute. In-page links and assets are the ones that get the relative treatment.

Example

templ Layout(c ssg.Ctx) {
	<!DOCTYPE html>
	<html lang="en">
		<head>
			<link rel="canonical" href={ c.URL() }/>
			<link rel="stylesheet" href={ c.Asset("style/style.css") }/>
		</head>
		<body>
			{ children... }
			<a href={ c.Link("about/") }>About</a>
		</body>
	</html>
}

The three URL builders cover every case: URL for anything that must be absolute, Asset for files, Link for pages.