Hugo lets you generate menus from the
menu parameters of individual pages. For example, if you have a Menu
example-menu with a top page, Projects 1 and 2 directly beneath it, two examples under Project 1, it will have the following structure:
example-menu └── Top ├── Project 1 │ ├── Example 1 │ └── Example 2 └── Project 2
This menu can be created using the following frontmatter structure:
--- menu: example-menu: name: "Top" ---
--- menu: example-menu: parent: "Top" name: "Project 1" weight: 10 ---
--- menu: example-menu: parent: "Project 1" name: "Example 1" weight: 10 ---
--- menu: example-menu: parent: "Project 1" name: "Example 2" weight: 20 ---
--- menu: example-menu: parent: "Top" name: "Project 2" weight: 20 ---
So, how do we render this? The Hugo documentation shows some good examples of a menu template, but it also has a few shortcomings:
The menu is only two levels deep. The examples in
example-menuwill never be visible.
- It shows the entire menu at once. If the menu contained items with lots of children, it will be pretty difficult to navigate.
You can definitely see the result of this from the official Hugo website as well, just have a look at how many pages there are in the Functions section!
Inspired by this Vue example case, I decided to design a recursive menu template that can:
- display arbitrarily nested menus (and only the designers would stop us), and
- show only the parents and siblings of the current page.
Let’s start with having a look at the overall structure first. This is where the menu list sits inside
You can see that the partial is called with three variables,
menuis the name of the menu. In our example it has the value
example-menu. This is used as the first argument in
listcontains the children pages to be displayed. The initial value,
index .Site.Menus $menu_idwill pull all the top-level pages in the menu. In our example, this will be
currentis the current page. Because of how Hugo scopes work, you will find many partials and shortcodes start by first saving the current page, which is done here as well.
Let’s look inside what the partial looks like.
It definitely wasn’t as tricky as it sounded!
Here would be the result of rendering
example-menu from the Project 2 page.
Update The menu template was originally developed from the example template in the Hugo documentation, which renders like this:
<ul> <li>Item 1</li> <ul> <li>Subitem 1</li> <li>Subitem 2</li> </ul> <li>Item 2</li> </ul>
<ul> <li> Item 1 <ul> <li>Subitem 1</li> <li>Subitem 2</li> </ul> </li> <li>Item 2</li> </ul>
Hugo documentation is now fixed as well.