Menu builder
In our menus recipe, we show how to build several types of menus with Kirby. However, those examples work with main pages and their children and have their limits when it comes to building more flexible menus where the depth of a page is not important or where you need multiple menus with selected pages.
In this recipe, we will look how we can build such custom menus where editors can pick the pages they want to include in a menu, or how we can even include external links.
In this recipe we assume that these menus are created in site.yml
, so that we use the $site
object when rendering them in the template. You can of course also use these menus in a page and adjust the template code accordingly.
Independent single level custom menu
The easiest way to create a custom menu from existing pages is the use of a pages field:
This setup can be used for a main menu, or for different single-level menus in the footer or in a sidebar.
In the template:
Multiple independent custom menus
The above works well if the required menus are known in advance, so that we can provide a field per menu.
In some cases, for example for a footer with multiple menus, we need some more flexibility. In such a case, where the number of menus can vary, a structure field is more useful.
In the template, we can use it like this to render each menu with its headline:
We loop through the structure items and create a new nav
tag for each menu. Inside the nav
tag, we render the title for each menu and the navigation items as a list.
Mixed menu I
What if we want mix external links with Kirby pages in our menu? We can also achieve this with a structure field in combination with the link field:
The link field allows us to add different kinds of links via a single field type. Here we limit the options to page links and external URLs, but if you want, you can allow other options.
In the template:
In our template, we loop through all items and convert the link field value to a valid URL. We render the title from the structure item if it is given, otherwise we fall back to the link field value. It might make sense to require the linkTitle
to prevent rendering page UUIDs as anchor text.
Two-level custom menu
In our blueprint, we create a structure field with the following setup:
The structure field has a pages field for the first level of menu items, and a second conditional pages field for the submenu items that is only shown to editors if they set the hasSubmenu
toggle field to true
. The toggle field is not absolutely necessary but helps to keep the form concise.
The mainMenu
pages field is limited to max: 1
pages, whereas the subMenu
field can have multiple subpages.
We can now render this menu in the template like this:
First we check if our structure field contains items with $menu->isNotEmpty()
, because we don't want to render empty nav
tags.
We then loop through the structure items, and for each first level item we check if the stored value is actually a page.
For the second level menu items, we also check if the pages collection ($item->subMenu()->toPages()
) is empty, and only render the submenu if there are items.
If you need more menu levels, you can extend this example and nest another structure field inside the first level structure.
Mixed menu II
We can also combine this example with external links like in the example before.
Since our fields are repetive, we split them into two parts.
The first part is our nested menu structure field:
Inside this field, we reuse a group of fields we have defined in /site/blueprints/fields/menufields.yml
, because we need these 4 fields in both structure levels:
This example allows us to have conditional external links both on the first and on the second levels of the menu. Editors can also add an optional link title.
Let's see how we can handle this field definition in our template. To keep our code dry, we use a snippet inside the template:
Inside the snippet, we recursively call the snippet for the submenu items:
As always, this is just a basic collection of ideas that you can extend for your use cases. In the structure field examples, you could, for example, include additional fields for link attributes, like target: _blank
for those clients who absolutely want to open links in new tabs.
Instead of using conditional fields for page links vs. external links, you could also use the link field plugin.