Skip to main content

URLFor & ID Generation

structpages provides four related helpers for generating URLs and DOM identifiers that stay in sync with the route tree:

  • URLFor(target) — build a URL for a page from its struct (pointer to a leaf, or Ref).
  • Ref{...} — dynamic reference for cases the static type lookup can't handle.
  • ID(target) — raw HTML id attribute string for a page or component.
  • IDTarget(target)#-prefixed CSS selector for HTMX hx-target.

All four are checked at build time by the structpages-lint analyzer.

URLFor

Generate a URL by passing a pointer to the target page struct:

href := structpages.URLFor(ctx, &productPage{})
// → "/products"

If the route has path parameters, pass them as additional arguments in tag order:

href := structpages.URLFor(ctx, &productPage{}, "p-123")
// → "/products/p-123"

[]any chain for nested params

When the target is deeply nested with parameters at multiple levels, pass the chain as a single []any:

// route: /orgs/{org}/products/{id}/edit
href := structpages.URLFor(ctx, &editPage{}, []any{"acme", "p-123"})
// → "/orgs/acme/products/p-123/edit"

This disambiguates from the variadic form when a single argument is itself a slice.

Inheriting params from the current request

If the current request already has matching path params in its context, URLFor reuses them automatically. You only need to pass params that differ from the current route:

// Inside a handler for /orgs/{org}/products/{id}, where org="acme", id="p-123":
href := structpages.URLFor(ctx, &siblingPage{})
// → "/orgs/acme/products/p-123/sibling" (params inherited)

Strict mode (default)

URLFor is strict by default: if a required param isn't provided and can't be inherited from the request, it returns an error rather than silently emitting a broken URL like /orgs//products//edit. Boot-time validation via URLForValidate lets you fail fast in tests.

Ref

When the target page can't be referenced by static type (e.g. you have multiple identical leaf types at different routes), use Ref:

href := structpages.URLFor(ctx, structpages.Ref{Name: "admin.productPage"})

Ref resolves by the fully-qualified node name in the page tree. The lint analyzer also verifies Ref strings.

ID and IDTarget

For HTMX, you want consistent DOM ids that match across server-rendered HTML and client-side hx-target selectors:

// In the template:
<div id={ structpages.ID(&commentList{}) }>...</div>

// In a form posting to a partial endpoint:
<form hx-post="/comments" hx-target={ structpages.IDTarget(&commentList{}) }>

ID returns "commentList"; IDTarget returns "#commentList". Use IDTarget for hx-target (it expects a CSS selector) and ID for the actual id attribute on the element.

Build-time checking

Install the analyzer:

go install github.com/jackielii/structpages/tools/lint/cmd/structpages-lint@latest
structpages-lint ./...

It catches:

  • URLFor calls with wrong param count for the target route.
  • Ref strings that don't resolve to any page.
  • ID / IDTarget calls against types that aren't mounted.
  • Mismatches between URLFor target and the surrounding Mount tree.

Wire it into CI alongside go vet for fast feedback.

See also