How to add a dynamic navigation menu to your hugo website

In a previous post we looked at how to create a static navigation menu for your hugo website. This is great, but like most websites, menus will rarely be static. And hugo provides a beautiful way of defining naviagtion menu’s available to the theme from within the config.toml file of your project.

The following code is going to give us everything we need to build a dynamic menu for your Hugo website.

  {{- with .Site.Menus.main }}
  <div class="main-navigation">
    <nav class="navigation">
      <div class="main-navigation-container relative">
        <div class="mobile-nav-close">
          <span class="mobile-nav-close-icon"></span>
        </div>
        <ul class="navigation-list">
          {{ $home := $.IsHome}}
          {{- range $item := . }}
          <li class="navigation-item">
            {{ if eq $.Name $item.Name }}
            <a aria-current="page" class="navigation-link navigation-link--current" href="{{ $item.URL }}">{{ $item.Name
              }}</a>
            {{ else if (and ($home) (eq $item.URL "/"))}}
            <a aria-current="page" class="navigation-link navigation-link--current" href="{{ $item.URL }}">{{ $item.Name
              }}</a>
            {{ else }}
            <a aria-current="false" class="navigation-link" href="{{ $item.URL }}">{{ $item.Name }}</a>
            {{ end }}
          </li>
          {{- end }}
        </ul>
      </div>
    </nav>
  </div>
  <div class="mobile-nav-open">
    <span class="mobile-nav-open-icon"></span>
  </div>
  {{- end }}

If you just wanted to copy and paste the goodness, the it does make one assumption.

That You have a menu defined in your website’s config.toml file called main

[menu]
[[menu.main]]
  identifier = "home"
  name = "Home"
  url = "/"
  weight = 1
[[menu.main]]
  identifier = "categories"
  name = "Categories"
  url = "/categories"
  weight = 2
[[menu.main]]
  identifier = "tags"
  name = "Tags"
  url = "/tags"
  weight = 3

So… Whats really going on here?

Well in short, nothing particularly complicated but if you’d like a run-through then read on and I’ll explain.

Firstly we open up the conditional logic that IF if there is a menu called main within Global Site variable, then we’re going to output some html.

the block of Hugo templating code line might look a little more cryptic, but fear not - it’s still pretty simple stuff.

 {{ $home := $.IsHome}}
 {{- range $item := . }}
    <li class="navigation-item">
      {{ if eq $.Name $item.Name }}
        <a aria-current="page" class="navigation-link navigation-link--current" href="{{ $item.URL }}">{{ $item.Name}}</a>
      {{ else if (and ($home) (eq $item.URL "/"))}}
        <a aria-current="page" class="navigation-link navigation-link--current" href="{{ $item.URL }}">{{ $item.Name}}</a>
      {{ else }}
        <a aria-current="false" class="navigation-link" href="{{ $item.URL }}">{{ $item.Name }}</a>
      {{ end }}
    </li>
{{- end }}

All thats happening here is that we’re opening up a loop with our range keyword, and then we assign the current scope of the loop to a variable which we’ve named item.

For each item, we output a html <li> and then we go on to check if the current page the visitor is viewing is equal to the current menu item we’re evaluating in the loop. If it is the same, output and ARIA attribute to let screen readers know that the menu item is the current page. otherwise, we still output a html <li>, but with the same ARIA attribute letting readers know that item is NOT the current page being viewed.

You don’t NEED this extra logic but its always nice to consider a11y.