Explanation of rendering order

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

  1. Shortcodes are rendered inside out (children before parents)
  2. Shortcodes are rendered in random order, except if they are nested inside another shortcode
  3. Shortcodes are rendered before templates
  4. 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 :slight_smile:
  • Also posted at https://www.liwen.id.au/hugo-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
    • 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.

8 Likes

@rdwatters should we have something on this subject in the docs? I can draft something if you advise me on the appropriate level of detail.

This is REALLY REALLY REALLY elucidating and helpful. Great job on this, I learned a lot. Would love to see more use-cases for this stuff.

Thanks!

Really awesome.

Thanks for the info.

schmorrison