Skip to content

Commit 9a5079d

Browse files
committed
content: Improve documentation for partial decorators
Closes gohugoio#3376
1 parent 198e5f1 commit 9a5079d

File tree

8 files changed

+189
-21
lines changed

8 files changed

+189
-21
lines changed
Lines changed: 53 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,71 @@
11
---
22
title: templates.Inner
3-
description: Inner executes the inner content of a partial decorator.
3+
description: Executes the content block enclosed by a partial call.
44
categories: []
5-
keywords: []
5+
keywords: [decorator]
66
params:
77
functions_and_methods:
88
aliases: [inner]
99
returnType: any
10-
signatures: [templates.Inner]
10+
signatures: ['templates.Inner [CONTEXT]']
1111
---
1212

1313
{{< new-in 0.154.0 />}}
1414

15-
> [!note]
16-
> We will improve documentation on this ... next year! But take it for a spin.
15+
The `templates.Inner` function defines the injection point for code nested within a block style partial call. This is the core mechanism used to create a [partial decorator][].
1716

18-
`templates.Inner` can be called zero or more times in a partial template, typically with different data (e.g. pages in a range), and its presence signals a reversal of the execution -- the callee becomes the caller. This only works for partials wrapped in a `with` block (see example). Decorators can be deeply nested, see [this PR](https://github.com/gohugoio/hugoDocs/pull/3330) for an example.
17+
## Overview
1918

20-
A very simple (and not very useful) example of a [partial decorator](g):
19+
The `templates.Inner` function acts as a placeholder within a partial template. When a partial is called as a decorator, it captures a block of code from the calling template rather than rendering it immediately. The `templates.Inner` function tells Hugo exactly where to inject that captured content.
2120

22-
```go-html-template
23-
{{ with partial "b.html" "World" }}Hello {{ . }}{{ end }}
24-
{{ define "_partials/b.html" }}<b>{{ inner . }}</b>{{ end }}
21+
This signals a reversal of execution where the callee becomes the caller. The partial manages the outer structure while the calling template remains in control of the inner content.
22+
23+
## Usage
24+
25+
To use this function, the calling template must use the block style syntax with a [`with`][] statement. This allows decorators to be deeply nested.
26+
27+
```go-html-template {file="layouts/home.html"}
28+
{{ with partial "components/card.html" . }}
29+
<p>This content is passed to the partial.</p>
30+
{{ end }}
2531
```
2632

27-
The above renders to:
33+
Inside the partial, call `templates.Inner` to render the captured block.
2834

29-
```handlebars
30-
<b>Hello World</b>
35+
```go-html-template {file="layouts/_partials/components/card.html"}
36+
<div class="card-frame">
37+
{{ templates.Inner . }}
38+
</div>
3139
```
40+
41+
## Arguments
42+
43+
The function accepts one optional argument: the [context](g). This argument determines the value of the dot (`.`) inside the captured block when it is rendered.
44+
45+
- If you provide an argument, such as `{{ templates.Inner .SomeData }}`, the dot inside the captured block is rebound to that specific data.
46+
- If you do not provide an argument, the captured block uses the context of the caller where the partial was first invoked.
47+
48+
## Context and scope
49+
50+
When using decorators, the `with` statement creates a new [scope](g). Variables defined outside the with block in the calling template are not automatically available inside the captured block.
51+
52+
By passing a context to `templates.Inner`, you ensure that the injected content has access to the correct data even when nested inside multiple layers of wrappers. This is critical when the decorator is used inside a loop or a specific data overlay.
53+
54+
## Repeated execution
55+
56+
A decorator can execute the captured content zero or more times. This is useful when the wrapper needs to repeat the same decoration for a collection of items, such as a list or a grid.
57+
58+
```go-html-template {file="layouts/_partials/list-decorator.html"}
59+
<ul class="styled-list">
60+
{{ range .items }}
61+
<li>
62+
{{ templates.Inner . }}
63+
</li>
64+
{{ end }}
65+
</ul>
66+
```
67+
68+
In this example, the code provided by the caller is rendered once for every item in the .items collection, with the dot . updated to the current item in each iteration.
69+
70+
[`with`]: /functions/go-template/with/
71+
[partial decorator]: /templates/partial-decorators/
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
---
22
title: partial decorator
3+
reference: /templates/partial-decorators/
34
---
45

5-
A _partial decorator_ is a [_partial_](g) template called from any other template including [_shortcodes_](g), [render hooks](g), and other partials.
6+
A _partial decorator_ is specific type of [_partial_](g) that functions as a [_wrapper component_](g). While a standard partial simply renders data within a fixed template, a decorator uses composition to enclose an entire block of content. It utilizes the [`templates.Inner`][] function as a placeholder to define exactly where that external content should be injected within the wrapper's layout.
67

7-
See [templates.Inner](/functions/templates/inner/) for more information.
8+
[`templates.Inner`]: /functions/templates/inner/
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
title: wrapper component
3+
reference: /templates/partial-decorators/
4+
---
5+
6+
A _wrapper component_ is an interface pattern that encloses other content through composition rather than fixed parameters. It provides a reusable shell to handle layout, styling, or logic, allowing the calling template to inject arbitrary content into the component's interior.

content/en/templates/404.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ linkTitle: 404 templates
44
description: Create a template to render a 404 error page.
55
categories: []
66
keywords: []
7-
weight: 190
7+
weight: 200
88
---
99

1010
To render a 404 error page in the root of your site, create a 404 template in the root of the `layouts` directory. For example:

content/en/templates/embedded.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ title: Embedded partial templates
33
description: Hugo provides embedded partial templates for common use cases.
44
categories: []
55
keywords: []
6-
weight: 170
6+
weight: 180
77
aliases: [/templates/internal]
88
---
99

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
---
2+
title: Partial decorators
3+
description: Use partial decorators to create reusable wrapper components that enclose and compose template content.
4+
categories: []
5+
keywords: [decorator]
6+
weight: 170
7+
---
8+
9+
## Overview
10+
11+
{{% glossary-term "partial decorator" %}}
12+
13+
This approach creates a connection between two files. The calling template provides a block of code and the partial decorator determines where that code appears. This allows the partial to wrap around content without needing to know the specific markup or internal logic of the enclosed block.
14+
15+
## Implementation
16+
17+
To use a partial decorator, use a block-style call in your templates. The [`with`][] statement is required to initiate the partial and create a container for the content. This block can include any valid template code including page methods and functions.
18+
19+
```go-html-template {file="layouts/home.html" copy=true}
20+
{{ with partial "components/wrapper.html" . }}
21+
<p>Everything in this block will be wrapped.</p>
22+
<p>{{ .Content | transform.Plainify | strings.Truncate 200 }}</p>
23+
{{ end }}
24+
```
25+
26+
Inside the partial template, place the `templates.Inner` function call where the wrapped content should appear.
27+
28+
```go-html-template {file="layouts/_partials/components/wrapper.html" copy=true}
29+
<div class="wrapper-styling">
30+
{{ templates.Inner . }}
31+
</div>
32+
```
33+
34+
The `with` statement creates a new [scope](g). Variables defined outside of the `with` block are not available inside it. To use external data within the wrapped content, you must ensure it is part of the [context](g) passed in the partial call.
35+
36+
A key feature of the `templates.Inner` function is its ability to accept a context argument. By passing a context to the function, you define what the dot (`.`) represents inside the wrapped block. This ensures that the injected content has access to the correct data even when nested inside multiple layers of wrappers.
37+
38+
## Benefits of composition
39+
40+
Using partial decorators to build wrapper components provides several advantages:
41+
42+
- It eliminates the need to use separate partials for opening and closing tags when encapsulating a block of code.
43+
- It prevents parameter bloat because a standard partial no longer requires an extensive list of arguments to account for every possible variation of the content inside it.
44+
- It enables clean composition where the wrapped block can execute any template logic without the wrapper needing to receive or process that data.
45+
46+
This approach separates container logic from content logic. The wrapper handles structural requirements like specific class hierarchies or CSS grid containers. The calling template retains control over the inner markup and how data is displayed.
47+
48+
## Example
49+
50+
The following templates illustrate how to nest three wrapper components including a section, a column, and a card while passing context through each layer.
51+
52+
The home template initiates the structure by calling the section, column, and card partials as decorators:
53+
54+
```go-html-template {file="layouts/home.html" copy=true}
55+
{{ with partial "components/section.html" (dict "page" . "label" "Recent Posts") }}
56+
<div class="grid-wrapper">
57+
{{ range site.RegularPages }}
58+
{{ with partial "components/column.html" (dict "page" . "class" "col-half") }}
59+
{{ with partial "components/card.html" (dict "page" . "url" .RelPermalink "title" .LinkTitle) }}
60+
<p>
61+
{{ .Content | plainify | strings.Truncate 240 }}
62+
</p>
63+
{{ end }}
64+
{{ end }}
65+
{{ end }}
66+
</div>
67+
{{ end }}
68+
```
69+
70+
The section component provides a semantic container and an optional heading:
71+
72+
```go-html-template {file="layouts/_partials/components/section.html" copy=true}
73+
<section class="content-section">
74+
{{ with .label }}
75+
<h2 class="section-label">
76+
{{ . }}
77+
</h2>
78+
{{ end }}
79+
80+
<div class="section-content">
81+
{{ templates.Inner .page }}
82+
</div>
83+
</section>
84+
```
85+
86+
The column component manages layout width by applying a CSS class:
87+
88+
```go-html-template {file="layouts/_partials/components/column.html" copy=true}
89+
<div class="{{ .class | default "column-default" }}">
90+
{{ templates.Inner .page }}
91+
</div>
92+
```
93+
94+
The card component defines the visual boundary for the content:
95+
96+
```go-html-template {file="layouts/_partials/components/card.html" copy=true}
97+
<div class="card">
98+
{{ with .title }}
99+
<h2 class="card-title">
100+
{{ if $.url }}
101+
<a href="{{ $.url }}">{{ . }}</a>
102+
{{ else }}
103+
{{ . }}
104+
{{ end }}
105+
</h2>
106+
{{ end }}
107+
108+
<div class="card-body">
109+
{{ templates.Inner .page }}
110+
</div>
111+
112+
{{ with .url }}
113+
<div class="card-footer">
114+
<a href="{{ . }}">Read more</a>
115+
</div>
116+
{{ end }}
117+
</div>
118+
```
119+
120+
[`with`]: /functions/go-template/with/

content/en/templates/robots.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ linkTitle: robots.txt templates
44
description: Hugo can generate a customized robots.txt in the same way as any other template.
55
categories: []
66
keywords: []
7-
weight: 180
7+
weight: 190
88
aliases: [/extras/robots-txt/]
99
---
1010

data/keywords.yaml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@
77
# will decrease over time, though the initial implementation will require some
88
# effort.
99

10+
- decorator
11+
- filter
1012
- highlight
1113
- menu
12-
- random
13-
- resource
1414
- metadata
1515
- process
16-
- filter
16+
- random
17+
- resource

0 commit comments

Comments
 (0)