Jacob Kaplan-Moss

Today I Learned…

Recursion in Hugo Templates

Turns out recursion is possible in Hugo templates by define-ing blocks and then “calling” them with template. (I had thought define was only for overriding blocks from parent templates, like Django).

The use case was that I wanted to render a nested file tree, e.g.

- dir/
  - file1
  - file2
- dir2/
  - file3
  - dir3/
    - file4
    - file5

And so here’s a slimmed down version of what I figured out:

{{ define "main" }}
  ...
  <h2>All Pages:</h2>
  {{ template "navtree" . }}
  ...
{{ end }}

{{ define "navtree" }}
  <ul>
    {{ range .RegularPages }}
      <li><a href="{{ .RelPermalink }}">{{ .Title }}</a></li>
    {{ end }}
    {{ range .Sections }}
      <li>
        <a href="{{ .RelPermalink}}">{{ .Title }}
        {{ template "navtree" . }}
      </li>
    {{ end }}
  </ul>
{{ end }}

This separates out “regular pages” (i.e. individual documents, leaf nodes) from “sections” (directories). If you wanted to intermingle them, you could do something like this instead:

{{ range .Pages }}
  <li>
    <a href="{{ .RelPermalink }}">{{ .Title }}</a>
    {{ if.IsSection }}
      {{ template "navtree" . }}
    {{ end }}
  </li>
{{ end }}

Bonus TIL: if you want to nest content more than one level, e.g. foo/bar/wow.md, and have that nesting show up in your section hierarchy, you’ll need an _index.md in every level except the top. That is, foo/_index.md is optional, but if you’re missing foo/bar/_index.md, all the content directory will be considered part of the foo collection, and you won’t have a foo/bar collection.