Pages and PageMeta

The page signature, and every field of the metadata record.

Updated 2 min read

proc page(ctx: Context, ref meta: PageMeta): string

meta is passed by ref because the page is expected to fill it in. The document shell reads it after the layout has run, so both the page and its layout can contribute.

Every field

FieldTypeEffect
titlestring<title>
descriptionstring<meta name="description">
langstring<html lang>, default "en"
canonicalstring<link rel="canonical"> when set
iconstring<link rel="icon"> when set
bodyClassstringclass on <body>
stylesheetslist(string)One <link rel="stylesheet"> each
scriptslist(string)One <script type="module"> each
needsClientRuntimeboolSet by island
statusintThe response status, default 200

Status is what makes an error page honest

A page that renders "not found" should say so in the status line, not just in the heading:

if !PostStore.find(id, post) {
  meta.status = 404;
  meta.title = "Post not found";
  h.el("h1", "Post not found");
  return h.done();
}

Search engines, monitoring and caches all read the status line. A 200 that says "not found" in body text is a lie the whole stack believes.

Throwing pages

A page or layout declared throws is wrapped by the generated handler: an escaped error is logged and becomes a 500 rather than unwinding the connection task.

What the context carries

MemberReturns
pathParam(name, fallback = "")A captured route segment
paramInt(name, fallback)The same, parsed as an integer
queryParam(name, fallback = "")A query-string value
setLocal(name, value) / getLocal(name, fallback)Per-request scratch state
method() / path()The request method and normalised path
requestIdip:port#n, also used by the access log
requestThe parsed request

ctx.request carries header(name), contentType(), bodyText(), accepts(mime), clientIp(), query, headers, peerIp and peerPort.

clientIp() reads X-Forwarded-For and is spoofable unless a trusted proxy overwrites it. Never use it for authorization.