Menu structure, with full menu tree or drop downs

Coming from Wordpress and with little in-depth IT-knowhow, I am trying to convert my existing websites (for instance at to Hugo. I do not need the exact same look & feel that you see at, actually I quite like the clean redlounge theme, but some thing I really would like to port over:

  • ability to run a YouTube movie inside the website, I go that working, with shortcodes
  • slideshows, got that working too with shortcodes,
  • tags in blog posts, that is standard feature in Hugo so that is solved,
  • forms. I never did anything in PHP but with some examples I got it working, PHP, Javascript and nocaptcha’s, yes I am very proud of myself
  • connected to Google Analytics, threw some code in a footer and boom, works
  • a set of static pages with a multi-level menu. Here I get stuck.

I tried SectionPagesMenu = “main” in config.toml, and this nicely creates menus, but I see two problems:

  1. no multi level menus, I only get to see the top level
  2. the “Thank you” exit page (for people that have completed a form) get’s listed because the “thank” file is somewhere in a section. I don’t want to see the “Thank you” page.

So I tried to mess around with custom menu’s , but really I have no idea how to iterate over a menu tree and display it, possible with drop down menu’s, or how to display just the applicable sub-menu structure in the sidebar.

I googled for solutions, maybe one is in front of my face but I just don’t see it. Anybody has an idea?

What I found when I coded my site (specifically the inner pages like was, that menus depended on the framework you’re using, or, whatever javascript menu you chose. For my feeble brain, it was hard work to figure it out. You might get some ideas looking at:

I referred to the doc, and to the materializecss (framework) documentation when I coded it.


1 Like

Thank Rick for writing this answer. I am a little busye at the moment, I hope this weekend I can find some time to go into the code and try to figure out how this all works. Thanks so far.

I spent about five hours trying to understand the code. Can’t wrap my head around this. I now see there similar code on the docs page here , but that didn’t make me understand this either.

I am afraid without in-depth knowledge of Hugo I will not get this working. Pity, because I thought I was almost there and I am very much ready to kick out Wordpress. Maybe I"ll have another try in a few weeks.

Thanks for the help anyways, much appreciated.

I sympathize. I imagine a good coder would rip through it, but it took me way too long.

I’m pretty new to Hugo/Go but I took a swing at it. It’s pretty hack-y and definitely not perfect but it gets me to my own requirements. I’d love if someone who knows the templating system could clean it up for me

Here’s a screenshot of what it looks like:

Some Caveats:

  • I’m grouping by section. You could easily just loop over all the pages and get the whole tree too (I think).
  • I got a pseudo-tree structure by sorting by dir and then splitting it up. This is not a true tree. It’s just <li> with more padding for deeper objects.
  • I opted to use multiple if statements instead of combining and/or logic statements for readability. and(or(this) or ()(((((((() … got me bummed out.
  • leaflevel and level are different because when I split the path I get an extra "" on the end of the array that I was able to just use for files. Folders don’t use it though.
  • This will only work in the simplest case I could think of. I didn’t test any fancy stuff.

Template code:

<!-- Loop over sections first -->
{{ range $name, $el := .Site.Sections }}
  <div class="col-sm-6">
    <div class="card card-block">
        <h4 class="card-title">{{ replace $name "_" " "}}</h4>
        <ul class="nav tree">
          <!-- Set up levels that we use for indents -->
          {{ $.Scratch.Set "level" 0 }}
          {{ $.Scratch.Set "leaflevel" 0 }}
          {{ $.Scratch.Set "name" $name }}

          <!-- Loop over pages sorted by DIR -->
          {{ range sort .Pages "Source.Dir" "asc"}}
            <!-- split the DIR using "/" -->
            {{ $arr := split .Source.Dir "/" }}
            <!-- Now loop over our split array-->
            {{ range $index, $name := $arr }}
              <!-- Already have the root in the H4 above -->
              {{if and (ne $name "") (gt $index 0) }}

                {{if gt $index ($.Scratch.Get "level") }}
                  {{ $.Scratch.Set "level" $index}}
                  {{ $.Scratch.Set "name" $name }}
                  <li class="nav-item dir indent{{$.Scratch.Get "level"}}"><span>{{ replace $name "_" " "}}<span></li>

                {{if and (eq $index ($.Scratch.Get "level")) ( ne ($.Scratch.Get "name") $name) }}
                  {{ $.Scratch.Set "level" $index}}
                  {{ $.Scratch.Set "name" $name }}
                  <li class="nav-item dir indent{{$.Scratch.Get "level"}}"><span>{{ replace $name "_" " "}}<span></li>
              {{end}} <!-- if -->
              {{ $.Scratch.Set "leaflevel" $index}}
            {{ end }} <!-- range -->
            <li class="nav-item indent{{$.Scratch.Get "leaflevel"}}">
              <a class="nav-link" href="{{ .Permalink }}">{{.Title}}</a>
          {{ end }} <!-- range -->
{{ end }} <!-- range -->

The css (scss in my case):

@for $i from 1 through 10 {
  li.indent#{$i} {
    padding-left: 0.7rem * ($i - 1);
1 Like

Don’t be so hard on yourself Rick. I’ve been in the business for nearly 20 years and I’m not ripping through anything when it comes to Hugo.

1 Like