Edit on GitHub
Jump to docs navigation

Configuration / Using menus

Note: You are currently reading the documentation for Bolt 3.7. Looking for the documentation for Bolt 5.2 instead?

Bolt has built-in functionality to create menus in your frontend templates. You can define one or more menus in the file app/config/menu.yml, which can then be inserted in your templates using the {{ menu() }} tag.

To change one or more of the menus, edit the file app/config/menu.yml. You can add more separate menus, if you wish, and each menu can have one level of items below it. See the default menu.yml for an example of the supported options:

main:
  - label: Home
    title: This is the first menu item. Fo shizzle!
    path: homepage
    class: first
  - path: entry/1
    label: Second item
    submenu:
      - label: Sub 1
        path: entry/2
      - label: Sub 2
        class: menu-item-class
        path: entry/3

In this case main is the name of the menu. The options are:

Option Description
label override the 'title' of the record with a defined label. If omitted, the 'title' of the record is used.
title used as a 'title'-attribute in the rendered HTML. If omitted this can be substituted for the subtitle-field in a record.
class used to define an HTML class-attribute
path The 'path' to a record in Bolt, or a group of records. For example path: page/about will make this item link to a record of type 'page' with the slug 'about'. path: page/1 will link to the 'page' with id '1'. path: entries will link to the /entries overview page.
link define an external link to another site. For example link: https://bolt.cm. Do not use link together with path!
submenu defines a submenu. In the submenu you can define other items, with the same options as before.

To insert a menu in your templates, use

{{ menu() }}

If you have more than one menu, you should use its name to make sure you get the intended one:

{{ menu('foo') }}

By default, the menu is rendered using the template /app/theme_defaults/_sub_menu.twig. You can 'override' the default by copying this file to the root of your own theme folder. Bolt will pick your own version, and then it will not be overwritten in a future update. However, it is good practice to explicitly state which template file should be used to render a menu. Like this:

{{ menu('foo', 'partials/_sub_menu.twig') }}

or

{{ menu('foo', '/partials/_menu_foo.twig') }}

You can specify other parameters besides the menu name and the template. For example, you can also set the the ul class or whether or not the output should contain submenus. You can control these using the so-called named arguments in Twig. For example:

{{ menu(
    identifier = 'foo',
    template = 'partials/_menu_foo.twig',
    params = {'withsubmenus': false, 'class': 'myclass'}
) }}

Which is equivalent to this shorthand version:

{{ menu('foo', 'partials/_menu_foo.twig', {'withsubmenus': false, 'class': 'myclass'}) }}

Doing this will render the menu foo, using the template _menu_foo.twig. The filename can be anything, but it's good practice to prefix it with _menu, so it's always easily recognizable later, or to other people working with your HTML.

Note: You can define more than one menu in your menu.yml file, but you should define only one menu in each template file. So, if you have multiple menus that should be rendered with different HTML, you should have as many _menu_menuname.twig files in your theme.

A detailed example

In this section we'll show you a somewhat more elaborate example of how you can create a menu, with submenus. First, start by adding a small menu to your app/config/menu.yml-file:

test:
  - label: Bolt
    link: https://bolt.cm
  - label: Example org
    link: http://example.org
  - label: Silex
    link: http://silex.sensiolabs.org

As you can probably guess, this menu does nothing but provide links to three external websites. To get started, edit the template where you want this menu. Usually, menus are used in 'headers', 'footers' or 'aside' includes, but you can use them anywhere. For now, just insert this code, somewhere:

{{ menu('test', 'partials/_menu_test.twig') }}

This inserts the menu, using the template partials/_menu_test.twig template. The file probably isn't present yet, so create it in your own theme/-folder.

<ul>
{% for item in menu %}
    <li>
        <a href="{{ item.link }}">{{item.label}}</a>
    </li>
{% endfor %}
</ul>

Refresh a page that uses the template that you've added the {{ menu() }}-tag to in your browser, and you should see a very simple menu, with the following HTML-markup:

<ul>
    <li>
        <a href="https://bolt.cm">Bolt</a>
    </li>
    <li>
        <a href="http://example.org">Example org</a>
    </li>
    <li>
        <a href="http://silex.sensiolabs.org">Silex</a>
    </li>
</ul>

As you can see, the {% for %}-loop iterated over all of the items in the menu-array, and wrote out the HTML that you specified. Let's change our menu, so it has a submenu, listing some content on our site. In this example, we'll assume that you have a pages ContentType, and that records 1, 2 and 3 exist. If they don't, just replace them with some contenttype/id pairs that you do have. Edit the app/config/menu.yml-file:

test:
  - label: Bolt
    link: https://bolt.cm
  - label: All pages
    path: pages/
    submenu:
      - path: page/1
      - path: page/2
      - label: last page
        path: page/3
        class: my_class
  - label: Silex
    link: http://silex.sensiolabs.org

Now, the menu template needs to be extended, so that the submenu is output as well. We'll do this by adding another {% for %}-loop. We'll wrap this loop in an {% if %}-tag to prevent Bolt from outputting empty lists in the HTML. For example:

<ul>
{% for item in menu %}
    <li class="{{ item.class }}">
        <a href="{{ item.link }}">{{item.label}}</a>
        {% if item.submenu is defined %}
            <ul>
            {% for item in item.submenu %}
                <li class="{{ item.class }}">
                    <a href="{{ item.link }}">{{item.label}}</a>
                </li>
            {% endfor %}
            </ul>
        {% endif %}
    </li>
{% endfor %}
</ul>

The output in HTML might look like this now:

<ul>
    <li class="">
        <a href="https://bolt.cm">Bolt</a>
    </li>
    <li class="">
        <a href="/pages">All pages</a>
            <ul>
                <li class="">
                    <a href="/page/sic-consequentibus-vestris">Sic consequentibus vestris</a>
                </li>
                <li class="">
                    <a href="/page/sublatis-prima-tolluntur">Sublatis prima tolluntur</a>
                </li>
                <li class="my_class">
                    <a href="/page/tria-genera-bonorum">last page</a>
                </li>
            </ul>
    </li>
    <li class="">
        <a href="http://silex.sensiolabs.org">Silex</a>
    </li>
</ul>

Dynamic menus

You can use other menu.yml parameters to make a more dynamic menu. In this example we will use taxonomies combined with the menu to create taxonomy-based submenus. Let's say you want to have a few static pages to be listed as submenus under "Pages" in your menu.

Start with creating a new taxonomy in taxonomy.yml to control what pages are to be listed under "Pages":

menu:
    name: Menu
    singular_name: Menu item
    behaves_like: categories
    multiple: false
    options: [ about, pages, more ]

Then, in your menu.yml change your "Pages" to the following.

- label: Pages
      path: pages
      list:
          contenttype: pages
          where:
              menu: pages
              limit: 5

Now all that's left is to modify your submenu template (_sub_menu.twig) so that it adds the pages with the "pages" taxonomy.

{% macro display_menu_item(item, loop, extraclass, withsubmenus) %}
    {% from _self import display_menu_item %}
    {% spaceless %}
    <li class="index-{{ loop.index -}}
        {{ item.path|default('') == 'homepage' ? ' menu-text' -}}
        {{ loop.first ? ' first' -}}
        {{ loop.last ? ' last' -}}
        {{ (item.submenu|default(false) and withsubmenus) ? ' is-dropdown-submenu-parent' -}}
        {{ item|current ? ' active' }}">

        <a href="{{ item.link }}" title='{{ item.title|default('')|escape }}' class='{{ item.class|default('') }}'>
            {{- item.label|default('-') -}}
        </a>

        {% set list = [] %}

        {% if item.submenu is defined and withsubmenus %}
            <ul class="menu submenu vertical" data-submenu>
                {% for submenu in item.submenu %}
                    {{ display_menu_item(submenu, loop) }}
                {% endfor %}
                {% if item.list|default(false) %}
                    {% setcontent listedcontent = item.list.contenttype where item.list.where %}
                    {% for listitem in listedcontent %}
                        {% set list = list|merge([{title: listitem.title, link: listitem.link, label: listitem.title}]) %}
                    {% endfor %}
                    <ul class="menu submenu vertical" data-submenu>
                        {% for submenu in list %}
                            {{ display_menu_item(submenu, loop) }}
                        {% endfor %}
                    </ul>
                {% endif %}
            </ul>
        {% elseif item.list|default(false) %}
            {% setcontent listedcontent = item.list.contenttype where item.list.where %}
            {% for listitem in listedcontent %}
                {% set list = list|merge([{title: listitem.title, link: listitem.link, label: listitem.title}]) %}
            {% endfor %}
            {% if list is not empty %}
            <ul class="menu submenu vertical" data-submenu>
                {% for submenu in list %}
                    {{ display_menu_item(submenu, loop) }}
                {% endfor %}
            </ul>
            {% endif %}
        {% endif %}

    </li>
    {% endspaceless %}
{% endmacro %}

Further customizations

That's basically all there's to it. Since the menus use standard Twig tags, we can enhance the lists with extra features, to automatically give special classes to the first or last item, or highlight the 'current' page.

Some of the more commonly used 'tricks' are:

  • index-{{ loop.index }} - Add the current index of the loop, like index-1, index-2, etc.
  • {% if loop.first %}first{% endif %} - Output first, but only for the first item in the loop.
  • {% if loop.last %}last{% endif %} - Output last, but only for the last item in the loop.
  • {% if item|current %}active{% endif %} - Output current, but only if we're on the page that the item links to.
  • {% if item.title is defined %}title='{{ item.title|escape }}'{% endif %}
    • Add a title attribute, but only if it's defined in our .yml-file, or if the ContentType has a subtitle field.
  • {% if item.class is defined %}class='{{item.class}}'{% endif %} - Add a class attribute, but only it the item has a class defined in the .yml-file.

See the default /app/theme_defaults/_sub_menu.twig file for an in-depth example of all of the things you can do with menus. Remember that you should always copy this file to your own theme folder, or create your own from scratch. If you modify the default file, it will most likely get overwritten when you update Bolt to a newer version.

Normally you will only need the basic properties of each of the menu items, but sometimes you might need to do more with the items. For this reason, each item has access to the entire record. You can use {{ item.record }} like you would use any other record. For instance, {{ item.record.taxonomy }}, or {{ dump(item.record) }}.



Edit this page on GitHub
Couldn't find what you were looking for? We are happy to help you in the forum, on Slack or on Github.