Slots
GoHT composes templates with named slots using @slot, @ifslot, @noslot, and @eachslot.
@slotrenders named slot content, optionally with default content.@ifslotrenders its body only when the caller assigned that slot.@noslotrenders its body only when the caller did not assign that slot.@eachslotbinds each template assigned to a slot for iteration.
These directives work in Haml, Slim, and EGO templates. EGO uses command tags such as <%@slot ... %>.
For the single built-in children region and @render/@children composition, see Render and Children.
Named slots
Named slots are useful for layouts that have several independent regions. A slot renders content that the caller passes with a generated With<Slot> method.
The header, main, and footer slots above include default content. GoHT renders default slot content only when the caller does not provide content for that slot. The sidebar and notifications slots render nothing unless the caller provides matching slotted templates.
Slim slot declarations follow the same shape:
EGO slots use <%@slot ... %> or a block form when they have defaults:
Passing slot content
For each @slot a template declares, GoHT generates a fluent With<Slot> method on that template’s generated type. Call it with the template to render in that slot, then render the completed result:
Render now only takes context.Context and io.Writer; slot content is assigned beforehand with With<Slot>, not passed as extra arguments to Render.
Each With<Slot> call returns a new template value rather than mutating the receiver. This lets you prepare a common starting point and reuse it for several pages:
base still has only its header; overview and archive each keep that header and add their own main content.
Slotted templates can contain their own slots. This lets a page fill a layout slot and still expose nested regions inside that slot.
In this example, ProjectPage(project) fills the layout’s main slot. The ProjectSummary and ProjectActivity templates fill slots that are declared inside ProjectPage.
Multiple templates in one slot
Every With<Slot> method is variadic, so pass several templates directly when you have them individually:
When the items are already collected in a slice, wrap them in goht.Fragment and pass that as the slot value. A Fragment renders its templates in order and satisfies goht.Template itself:
Dynamic slot names
Generated templates also have a Slot(name string, templates ...goht.Template) method for callers that only know a slot’s name at runtime. It returns the same type as the matching With<Slot> method, so it can be chained the same way. Assigning a name that the template did not declare with @slot does not panic; it records the error and Render returns it before writing any output.
@ifslot, @noslot, and @eachslot
@ifslot <name> renders its body only when the caller assigned that slot, including an explicitly empty assignment. @noslot <name> is its opposite: it renders its body only when the caller did not assign that slot. @eachslot <template> in <slot> binds each template assigned to a slot to a loop variable; render it explicitly with @render.
@ifslot is not limited to guarding a bare @slot call; anything nested inside it, such as a heading, only renders when the slot was assigned. @noslot is a sibling directive, not a child of @ifslot — pairing an @ifslot <name> with a @noslot <name> right after it behaves like an if/else for that slot.
Rendered output with the slot assigned — @ifslot renders and @noslot is skipped:
Rendered output when notes is never assigned — @ifslot skips its whole body, and @noslot renders in its place:
A bare @slot can carry its own default content, as shown under Named slots, and that default renders whenever the caller doesn’t assign the slot. But once @slot is nested inside @ifslot — as notes is above — there’s no bare @slot call left to hang a default on: the unassigned case belongs entirely to @ifslot’s sibling now. The same is true once you reach for @eachslot, which has no default content of its own either. Reach for a sibling @noslot any time you need fallback content alongside @ifslot or @eachslot.
If you don’t need extra content solely for the assigned case, you don’t need @ifslot/@noslot at all — a bare @slot with nested default content already covers both cases:
@eachslot stands on its own; it doesn’t need @ifslot to guard it. If the slot wasn’t assigned, the loop simply runs zero times, so an unguarded @eachslot is safe:
Slim uses the same directive:
EGO uses a block-form command tag:
An unassigned items slot renders an empty <ul></ul> this way. As with any slot, pair @eachslot with a sibling @noslot items if you want fallback content for that case instead — the same pairing shown for @ifslot above.