Menu structure from file system

Hi folks. I’m trying to leverage Hugo for an internal documentation site. Said site will have, among other things, multiple separate trees/books or some sections but not all sections will be hierarchical.

I can build a hierarchical menu structure off of the Menu system. However, that requires manually tagging every single page with the correct menu hierarchy, giving it IDs, etc. That’s a lot of editorial work, much of which will be done by less technical people than me. And if I want to reorganize the page structure at any point it’s going to be a huge PITA to do. Basically that’s not viable from a user-experience perspective.

What I want to be able to do is build a tree based on the file structure of the markdown files. I haven’t figured out how I can accomplish that, however. That data doesn’t seem to be exposed in a useful way. I am looking for advice on how to make that data available so that I can skip having every one of my many hundreds of files having a manual menu item and structure definition, which is a no-go for my users.

For reference, here’s how I’m building a tree off of the menu:

<aside class="sidebar">
    {{ $currentPage := . }}
    {{ $theSection := .CurrentSection.Section }}
    {{ with $activeMenu := index .Site.Menus $theSection }}
        <ul>
            {{ range $activeMenu }}

                <li {{ if $currentPage.HasMenuCurrent $theSection . }}class="active"{{ end }}>
                <a href="{{ .URL }}">
                    {{ .Pre }}
                    <span>{{ .Name }}</span>
                </a>
                    {{ template "treesub" . }}
                </li>
            {{ end }}
        </ul>
    {{ end }}
</aside>

{{ define "treesub" }}
    {{ if .HasChildren }}
    <ul class="sub-menu">
        {{ range .Children }}
        <li>
            {{ .Pre }}
            <a href="{{ .URL }}">{{ .Name }}</a>
            {{ template "treesub" . }}
        </li>
        {{ end }}
    </ul>
    {{ end }}
{{ end }}

That is, map the current page’s section to a menu of the same name, then traverse down that recursively to build a menu. It’s a file tree-based version of that which I’m after.

Thanks for any pointers.

I suspect you can use Hugo’s nested sections for this. It requires a “_index.md” file in the lower-most folders, and then you can do something like this:

Thanks, bep. That got me a long way toward where I need to go. I’m still running into two issues, though.

Here’s my current code:

<aside class="sidebar">
{{ $home := .Site.Home }}
    <ul>
        {{ template "section-tree-nav" $home }}
    </ul>
</aside>

{{ define "section-tree-nav" }}
    {{ range $index, $element := .Sections.Reverse }}
        <li><a href="{{ .URL }}">{{ $element.Title }}</a>
            <ul>
                {{ range .Pages }}
                    <li><a href="{{ .URL }}">{{ .Title }}</a></li>
                    {{ template "section-tree-nav" $element }}
                {{ end }}
            </ul>
        </li>
    {{ end }}
{{ end }}

Problem 1: If I make the recursive template call to $element, I get a nice nested list but it seems to repeat numerous times. If I make it to ., sub-sections don’t get called at all. This one is probably just a dumb PEBCAK.

Problem 2: As written this gets me a tree of the entire content directory. That’s not what I’m looking for; I’m looking for a tree just for the branch of the site the current page is in.

As an example, if I have top level sections “plant”, “animal”, and “fungus”, and under animal I have “mammals”, “reptiles”, and “birds”, when I’m on the “cat” page I want to see “mammals”, “reptiles”, and “birds” as the top items in the menu that’s generated. I’m not clear how to trim it down like that.

The way I am doing a Conditional Section Menu is by displaying Hugo Menu Items based on a conditional check of the Menu Item .URL vs the Page .Type (Note: .Type is an alias of .Section).

So provided that you have configured a standard Hugo Menu for your Section and Nested Sections called .Menus.section (look into the Docs for Menu configuration -I’m not going into the specifics-)

You can do the following:

{{ range .Site.Menus.section }}   
{{ if (strings.Contains .URL $.Type) }}
<a href="{{ .URL }}">{{ .Name }}</a>   
{{ end }}   
{{ end }}
1 Like

Sure. You can adapt it to start at “.CurrentSection”. Sorry, but I cannot get into specific troubleshooting with you on this … prose level. My post was meant to give you a direction, not a ready-to-use solution.