How are children menu entries detected?

Hello everyone! New Hugo user here :slight_smile:
I am struggling to understand how the parent-child relationship between content files is established. I read through the documentation and the board, but I couldn’t find a thorough explanation anywhere.

I am trying to create a simple menu with sub-menus, and I am using the example provided here. However, I cannot manage to get sub-entries, and everything is rendered as part of the main menu. It seems that the {{ if .HasChildren }} check always fails in the template I am creating, thus I suppose the parent-child relationship between content entries is not correctly established.

At first I was expecting that it would simply be extrapolated from the folder hierarchy, but it doesn’t seem to be the case. Here’s the folder structure I am using:

content [folder]
  portfolio [folder]
    entry1.md
    entry2.md

I am trying to obtain a “portfolio” entry an the “entry1” and “entry2” sub-entries. From the documentation it seems that I have to express such relationship in the frontmatter of each entry (weird, shouldn’t it be straightforward from the folder structure?) and I wrote parent : "portfolio" in the entries frontmatter. Still, it doesn’t work.

Can someone with a bit more experience please enlighten me on how to properly express such relationship? :slight_smile:

Hi Michele,

Unfortunately, I’m not someone with a bit more experience. Quite the obvious – I’m someone who is stuck on the exact same problem. I have been going through the Hugo documentation / tutorial in order and all seems fine and then the “menu” page is where I’ve gotten stuck. There seems to be many gaps in the explanation and I couldn’t figure it out.

Instead of posting a near identical message just a mere 12 hours after you, I thought I’d tell you what I know and maybe someone can correct me/us.

From what I can tell, whether you get menus working depends on the theme you’re using. It has to properly support it. I’m playing with the ananke theme since that’s the one the tutorial is using.

From there, I know the menu comes from the top-level config.toml file and/or the front matter of the pages in question. (The “and/or” is where I am stuck…I can’t tell from the documentation whether you need to make a change to one or both. The documentation implies “or”…if so, then I don’t know what I’m doing wrong…)

For example, I have this in my config.toml:

[menu]
[[menu.main]]
    identifier = "abc"
    name = "Bar"
    url  = "/en/posts/bar/"
    weight = 1
[[menu.main]]
    identifer = "def"
    parent = "abc"
    name = "Foo"
    url  = "/en/posts/foo/"
    weight = 1

I presume this is correct… That is, each item has an identifier and if “foo” is a child of “bar”, then I used the “identifier” of bar as the parent of “foo”. At the moment, I see “Bar” in the menu, but I can’t click it to get a list.

I did play with the files’ front matter but I really don’t know what I’m doing as it wouldn’t make sense to repeat what I had in the config.toml.

(Just an aside to your query that it is “weird” that it isn’t straight-forward from the folder structure. I don’t think that’s weird since not everything in the folders might be linked. And there has to be a way to re-order things or exclude things; so it has to be specified somewhere, I just don’t quite understand where.)

I also don’t know if there’s a problem with the ananke theme?

Hopefully someone can help both of us! Sorry to take over your question – if anything I said helps you figure out the solution, please let me know.

Ray

Hi there,

When we talk about Hugo Menus, we usually mean the Hugo-specific Menus feature, which is discussed here: Menus | Hugo and not to be confused with the more generic navigation “menus” in general website development.

Having said that, there are several ways to generate a navigation system for your Hugo website.

  1. First is using the Section Menu for Lazy Bloggers feature discussed here: Menu templates | Hugo
  2. Second is by iterating through the content structure and building navigation that way
  3. Third is by using Hugo Menus.

In the second method, the navigation hierarchy is implied by the content structure.

Hugo Menus need to be explicitly defined. You can do this in your site config, or in your page frontmatter, or both.

Taking the example in the docs here: https://gohugo.io/content-management/menus/#add-non-content-entries-to-a-menu

menu:
  main:                              # 1
  - identifier: about                # 2
    name: about hugo
    url: /about/
...
  - name: getting started            # 3
    url: /getting-started/
...

# 1 defines a Menu named main. This means you can access its menu items through site.Menus.main.

# 2 and # 3 each define a menu item. Notice 2 has an identifier field. That is what you need to identify and define parent-child relationships in Hugo Menus. So for example, if you wanted to add a third menu item history and nest it under the about menu, you would do that by adding the following:

  - name: history            
    parent: about
    url: /about/company-history/

I believe it’s an “either/or”. You can define your menu item either in the config or in your content frontmatter. I’m not sure what happens if you have it defined in both places.

Having your menu definition in the content frontmatter allows you access to the .Page variable from the menu entry (doc).

The config menu definition allows you to include non-content menu items (eg external links) to your Menu structure.

Pretty much. You mention using the anake theme, and looking at the code that generates the menu, it does not iterate through the menu item children.

Oh! I see. So, while there is some overlap between the two options, there are situations where what I need to do can only be done with one method. So, I better be comfortable with either option and not just learn one. I missed that while reading the tutorial – thank you!

Ugh… (hitting head against the desk) I’ve been playing with menus for hours every few months (i.e., I give up and then go back again). After last night, I thought I should just ask for help.

I did not realise that the theme I was using could not generate a “pull down menu”. Without knowing how to read the code (not yet, anyway…that’s something I intend to do once I have something working), I just assumed that the one suggested by the tutorial could do it.

Now I know to stop blaming myself and I have an idea what to look for.

Thank you for your reply! :smiley:

Ray

Hello everyone! I managed to obtain a flexible and clean solution through the following.

Ensure every .md file to be listed in the menu has its explicit menu structure definition in its frontmatter:

menu:
  main:
    parent: 'your_menu_parent'

The parent ‘your_menu_parent’ must match either a .Name, .LinkTitle or .Titleexisting field of an existing page (thus an .md file). I found this info in the documentation.

Then in your template file you can range through menu entries with a pattern similar to the following:

{{ range .Site.Menus.main }}
  {{ if .HasChildren }}
    <!-- Menu entry has children, iterate through them -->
      {{ range .Children }}
        <!-- Do stuff with children -->
      {{ end }}
  {{ else }}
    <!-- Menu entry has NOT children, do stuff -->
  {{ end }}
{{ end }}

I don’t think it is necessary to set anything in config.toml (or .yaml). I used to, but removing menu-related settings still keeps the menu working.

On a side note, unfortunately I couldn’t find a way to make this template logic recursive, which would allow to have unlimited sub-menu levels. I guess it is an intrinsic limitation of the Go template engine?

1 Like

You could try this technique: Menu with unlimited levels

So, an important thing to realize is that in Hugo, a “Hugo Menu” is a particular “thing” (think object) – it will not act like a Page (or Section) will. We make Hugo Menus either through the site config or via a “Lazy Blogger” method (i.e. within page front matter), these Hugo Menus can then be used to make out HTML. (Alternatively, we can create our own logic and iterate over the site content and create some HTML that way.)

The cool thing is that these are not mutually exclusive, and you can combine them to, if anything, make your life easier (at least after your template is made).

I might suggest putting this in your /portfolio/_index.md :

menu :
   main :
        title : Projects
showChildrenInMenu : true

Then you just need to use this in your menu template (Without having to add anything to the individual project pages

<ul>
    {{ range .Site.Menus.main }}
        <li>
            <a href="{{ .Page.Permalink }}">{{ .Title }}</a>
             {{ if .Page.Params.showChildrenInMenu }}
             <ul>
               {{ range .Page.Pages }}
                  <li>{{ . }}</li>
               {{ end }}
             </ul>
        {{ end }}
      </li>
    {{ end }}
</ul>

Thanks for the follow-up! Good to see what you ended up doing!

Indeed, I realised that you don’t have to add anything to the config.toml (or .yaml) unless you have a menu item that doesn’t match a page. For example, if you about an “About” tab but no “About” page. But within that, you have things like “What we are” and “Where we are” as two separate pages. Of course, this might be a rare occurrence – one might want to create an “About” page with links to these two pages.

But I guess being able to add menu items to the config.toml/.yaml file allows this possibility, at least.

Ray