I think I’ve finally grokked how rendering order / parsing order works - I couldn’t find a comprehensive explanation anywhere so I decided to write one.
tl;dr
- Shortcodes are rendered inside out (children before parents)
- Shortcodes are rendered in random order, except if they are nested inside another shortcode
- Shortcodes are rendered before templates
- Templates are rendered in order (of appearance)
Notes
- I hope this is useful!
- I don’t know which behaviours are inherent to Hugo vs Golang html/template, or which are by design vs quirks vs undocumented features; I’ve just documented the behaviour I’ve observed.
- NB this is based on Hugo v0.19
- I’ve structured this post based on the way my brain works, which might be confusing for you if your brain works differently. Sorry about that; I’m always open to suggestions to make something objectively better.
- Please let me know if you have any corrections, suggestions, or further information to add
- Also posted at Hugo Rendering Order
1. Shortcodes are rendered inside out
If you have nested shortcodes, the child shortcodes are rendered before their respective parents.
This means:
- You can’t use
.Page.Scratch
to pass variables from a parent to a child shortcode. - Instead, use
.Parent
to access the parent shortcode’s scope from the child shortcode, e.g. use.Parent.Params
or.Parent.Get
to inherit parameters from the parent shortcode. - You can use
.Page.Scratch
(or.Parent.Scratch
) to pass information from the child to the parent shortcode, e.g. if you wanted to count the number of child shortcodes.
Example:
/layouts/shortcodes/child.html:
{{ .Page.Scratch.Add "count" 1 }}
Running Count: {{ .Page.Scratch.Get "count" }}
/layouts/shortcodes/parent.html:
Total Count: {{ .Page.Scratch.Get "count" }}
{{ .Inner }}
/content/test.md:
{{< parent >}}
{{< child >}}
{{< child >}}
{{< child >}}
{{< /parent >}}
Output:
Total Count: 3
Running Count: 1
Running Count: 2
Running Count: 3
2. Shortcodes called from the page scope are rendered in random order
If you call the same shortcode several times, Hugo will render a random one first, then render the subsequent ones in order (when it reaches the last shortcode it will loop back to the first one.)
Example:
/content/test.md:
{{< child >}}
{{< child >}}
{{< child >}}
{{< child >}}
{{< child >}}
Output (varies at random):
Running Count: 3
Running Count: 4
Running Count: 5
Running Count: 1
Running Count: 2
If you call two different shortcodes on the same page, the rendering order is completely random.
Example:
/content/test.md: (child2
is a copy of child
)
{{< child >}}
{{< child >}}
{{< child >}}
{{< child >}}
{{< child >}}
{{< child2 >}}
{{< child2 >}}
{{< child2 >}}
{{< child2 >}}
{{< child2 >}}
Output (varies at random):
Running Count: 1
Running Count: 8
Running Count: 3
Running Count: 4
Running Count: 5
Running Count: 9
Running Count: 6
Running Count: 7
Running Count: 10
Running Count: 2
This means:
- You generally can’t use
.Page.Scratch
to pass variables between shortcodes, unless you don’t care which order they are rendered in. - For example, you could use
{{ $.Page.Scratch.Add "count" 1 }}
to count the number of times a shortcode is used (as long as you’re only using this information in a template file, NOT in another shortcode), or to give each shortcode a unique number (as long as the numbers don’t need to be in order).
3. Shortcodes nested within another shortcode ARE rendered in order
Example:
/content/test.md: (child2
is a copy of child
)
{{< parent >}}
{{< child >}}
{{< child >}}
{{< child >}}
{{< child >}}
{{< child >}}
{{< child2 >}}
{{< child2 >}}
{{< child2 >}}
{{< child2 >}}
{{< child2 >}}
{{< /parent >}}
Output (consistent):
Total Count: 10
Running Count: 1
Running Count: 2
Running Count: 3
Running Count: 4
Running Count: 5
Running Count: 6
Running Count: 7
Running Count: 8
Running Count: 9
Running Count: 10
This means:
- If you do need a bunch of shortcodes to render in order (e.g. because you want to give each one a consecutive number), you can achieve this by wrapping them in a dummy parent shortcode.
4. Shortcodes are rendered before anything else
Example:
/layouts/partials/header.html:
... <title>I have {{ .Page.Scratch.Get "count" }} children</title> ...
/content/test.md: (as above)
Output:
... <title>I have 10 children</title> ...
Note this works even though in terms of page order, <title>
appears before the content which contains the shortcodes.
This means:
- You can use
.Page.Scratch
to pass variables from a shortcode to a template file, e.g. if you have some bulky css/js files that you want to load in/layouts/partials/header.html
iff a shortcode has been used.
5. Templates are rendered in order of appearance
Example:
/layouts/index.html:
... {{ .Page.Scratch.Add "count" 1 }}
index.html: {{ .Page.Scratch.Get "count" }}
{{ partial "head.html" . }} ...
/layouts/partials/head.html:
... {{ .Page.Scratch.Add "count" 1 }}
head.html: {{ .Page.Scratch.Get "count" }}
{{ partial "head_custom.html" . }} ...
/layouts/partials/head_custom.html:
... {{ .Page.Scratch.Add "count" 1 }}
head_custom.html: {{ .Page.Scratch.Get "count" }} ...
Output:
... index.html: 1 ...
... head.html: 2 ...
... head_custom.html: 3 ...
Summary
This means that for a page using the example files above, the overall order of rendering would be:
- 9: /layouts/_default/single.html
- 10: …
- 11: /layouts/partials/head.html
- 12: …
- 13: /layouts/partials/head_custom.html
- 14: …
- 15: …
- 16: /layouts/partials/header.html
- 17: …
- /content/test.md
- 8: /layouts/shortcodes/parent.html
- 5: /layouts/shortcodes/child.html
- 6: /layouts/shortcodes/child.html
- 7: /layouts/shortcodes/child.html
- 4: /layouts/shortcodes/parent.html
- 1: /layouts/shortcodes/child.html
- 2: /layouts/shortcodes/child.html
- 3: /layouts/shortcodes/child.html
- 8: /layouts/shortcodes/parent.html
- 18: …
- 19: /layouts/partials/footer.html
- 20: …
Noting that 1,2,3,4 and 5,6,7,8 could be the other way around, at random.