Adam O'Grady

Hugo Recursive Templates

I’ve been working with my partner Nikki recently to build her a small website she can use as a knowledge base for all her witch-y stuff. This includes tarot, herbalism, naturism, etc. She couldn’t find a decent CMS that met all the features that she wanted, or at least one that wouldn’t be a pain in the arse to get working. By the end of her searches she had settled on just making her own static pages in HTML and CSS when I suggested Hugo. The output would be the same, but you get access to a lot of extra features to make developing it a lot easier.

One of the things she wanted was a sidebar menu that displayed all the section links in her site. Her site is particularly in-depth and contains many categories (sections) which themselves can contain more sub-categories (sub-sections), in a hierarchical fashion. Thankfully Hugo handles the content and link generation side of this really easily, you can just create more directories inside directories and the default link structure Just Works™.

To solve the issue with the sidebar though, we turned to one of my favourite lessons from functional programming; recursion.

To build the sidebar, we created two partials. The first, sidebar.html was imported directly into our header template and looks like this:

<nav>
    <ul>
        {{ range .Site.Sections }}
            <li>
                <a href="{{ .Permalink }}">{{ .Title}}</a>
                    {{ partial "sidebar-recurse.html" . }}
                
            </li>
        {{ end }}
    </ul>
</nav>

The second section is of course called sidebar-recurse.html and looks like the following:

<ul>
    {{ range .Sections }}
        <li>
            <a href="{{ .Permalink }}">{{ .CurrentSection.Title }}</a>
                {{ partial "sidebar-recurse.html" . }}
        </li>
    {{ end }}
</ul>

As you can see from that above snippet, sidebar-recurse.html in fact calls itself to generate deeper levels of the sidebar list.

Thankfully, if there are no deeper levels available (no “.Sections” in the current context), then it won’t recurse any further, preventing us from overflowing the memory stack and recursing forever.

One keyword I used in that last paragraph “context” is an important part about how we got this to work. Hugo has a concept of a “context”, in which certain variables are available. The context is specified by a single period character (‘.’). You can then access variables inside the context, so .Sections is accessing the “Sections” variable of the current context.

So when we use {{ range .Sections }}, we are saying “for each of the elements of the list “Sections” in the current context, please do the enclosed. This is great for iterating over lists, but also creates a new context, which consists of the data around that list element. This way when we go {{ partial "sidebar-recurse.html" . }}, we are passing only the data about that list object to the partial, rather than the entire context of the site.

Hopefully this should give you a neat tip about Hugo that you can use in situations where you want to repeatedly drill down on a set of nested lists of unknown depth.