Share icon

In Drupal 8 Twig templates give extra power to drupal to add dynamic content in twig style, In this tutorial we can see how we can add the custom menu classes to menu ul li using menu.html.twig.

If you are creating your own theme or custom theme in drupal 8 then to overwrite the menu class we need to override the menu.html.twig template to our theme directory, so copy menu.html.twig form drupal core template to our theme directory

Core template path: [drupal_root]/core/modules/system/templates/menu.html.twig

Core template look like below, now our target is add add custom classes to ul and li - so follow the steps below --

{#
/**
 * @file
 * Default theme implementation to display a menu.
 *
 * Available variables:
 * - menu_name: The machine name of the menu.
 * - items: A nested list of menu items. Each menu item contains:
 *   - attributes: HTML attributes for the menu item.
 *   - below: The menu item child items.
 *   - title: The menu link title.
 *   - url: The menu link url, instance of \Drupal\Core\Url
 *   - localized_options: Menu link localized options.
 *   - is_expanded: TRUE if the link has visible children within the current
 *     menu tree.
 *   - is_collapsed: TRUE if the link has children within the current menu tree
 *     that are not currently visible.
 *   - in_active_trail: TRUE if the link is in the active trail.
 *
 * @ingroup themeable
 */
#}
{% import _self as menus %}

{#
  We call a macro which calls itself to render the full tree.
  @see https://twig.symfony.com/doc/1.x/tags/macro.html
#}
{{ menus.menu_links(items, attributes, 0) }}

{% macro menu_links(items, attributes, menu_level) %}
  {% import _self as menus %}
  {% if items %}
    {% if menu_level == 0 %}
      <ul{{ attributes }}>
    {% else %}
      <ul>
    {% endif %}
    {% for item in items %}
      <li{{ item.attributes }}>
        {{ link(item.title, item.url) }}
        {% if item.below %}
          {{ menus.menu_links(item.below, attributes, menu_level + 1) }}
        {% endif %}
      </li>
    {% endfor %}
    </ul>
  {% endif %}
{% endmacro %}

Add custom class without menu name

1) adding set class variable like this

{% 
    set menu_classes_ul = [ 
      'navbar-nav', 
    ] 
  %} 
  {% 
    set menu_classes_li = [ 
      'nav-item', 
    ] 
  %}

So our template will look like this -

{#
/**
 * @file
 * Default theme implementation to display a menu.
 *
 * Available variables:
 * - menu_name: The machine name of the menu.
 * - items: A nested list of menu items. Each menu item contains:
 *   - attributes: HTML attributes for the menu item.
 *   - below: The menu item child items.
 *   - title: The menu link title.
 *   - url: The menu link url, instance of \Drupal\Core\Url
 *   - localized_options: Menu link localized options.
 *   - is_expanded: TRUE if the link has visible children within the current
 *     menu tree.
 *   - is_collapsed: TRUE if the link has children within the current menu tree
 *     that are not currently visible.
 *   - in_active_trail: TRUE if the link is in the active trail.
 *
 * @ingroup themeable
 */
#}
{% import _self as menus %}

{#
  We call a macro which calls itself to render the full tree.
  @see https://twig.symfony.com/doc/1.x/tags/macro.html
#}
{{ menus.menu_links(items, attributes, 0) }}

{% macro menu_links(items, attributes, menu_level) %}
  {% import _self as menus %}
  {% 
    set menu_classes_ul = [ 
      'navbar-nav', 
    ] 
  %} 
  {% 
    set menu_classes_li = [ 
      'nav-item', 
    ] 
  %}
  {% if items %}
    {% if menu_level == 0 %}
    {{ attributes }}
      <ul{{ attributes.addClass(menu_classes_ul) }}>
    {% else %}
      <ul>
    {% endif %}
    {% for item in items %}
      <li{{ item.attributes.addClass(menu_classes_li) }}>
        {{ link(item.title, item.url) }}
        {% if item.below %}
          {{ menus.menu_links(item.below, attributes, menu_level + 1) }}
        {% endif %}
      </li>
    {% endfor %}
    </ul>
  {% endif %}
{% endmacro %}

Now clear all cache of drupal and see the classes are added

2) Add class with menu_name 

As mention in template "available variables":
 * - menu_name: The machine name of the menu.

we can also add the menu name to class for this you need to add the menu_name at two place:

First:

{{ menus.menu_links(items, attributes, 0) }}

like this 

{{ menus.menu_links(items, attributes, 0, menu_name) }}

Second:

{% macro menu_links(items, attributes, menu_level) %}

like this

{% macro menu_links(items, attributes, menu_level, menu_name) %}

So you whole template looks like this -

{#
/**
 * @file
 * Default theme implementation to display a menu.
 *
 * Available variables:
 * - menu_name: The machine name of the menu.
 * - items: A nested list of menu items. Each menu item contains:
 *   - attributes: HTML attributes for the menu item.
 *   - below: The menu item child items.
 *   - title: The menu link title.
 *   - url: The menu link url, instance of \Drupal\Core\Url
 *   - localized_options: Menu link localized options.
 *   - is_expanded: TRUE if the link has visible children within the current
 *     menu tree.
 *   - is_collapsed: TRUE if the link has children within the current menu tree
 *     that are not currently visible.
 *   - in_active_trail: TRUE if the link is in the active trail.
 *
 * @ingroup themeable
 */
#}
{% import _self as menus %}

{#
  We call a macro which calls itself to render the full tree.
  @see https://twig.symfony.com/doc/1.x/tags/macro.html
#}
{{ menus.menu_links(items, attributes, 0, menu_name) }}

{% macro menu_links(items, attributes, menu_level, menu_name) %}
  {% import _self as menus %}
  {% 
    set menu_classes_ul = [ 
      'navbar-nav', 'custom-menu-' ~ menu_name|clean_class, 
    ] 
  %} 
  {% 
    set menu_classes_li = [ 
      'nav-item', 'custom-menu-' ~ menu_name|clean_class, 
    ] 
  %}
  {% if items %}
    {% if menu_level == 0 %}
    {{ attributes }}
      <ul{{ attributes.addClass(menu_classes_ul) }}>
    {% else %}
      <ul>
    {% endif %}
    {% for item in items %}
      <li{{ item.attributes.addClass(menu_classes_li) }}>
        {{ link(item.title, item.url) }}
        {% if item.below %}
          {{ menus.menu_links(item.below, attributes, menu_level + 1) }}
        {% endif %}
      </li>
    {% endfor %}
    </ul>
  {% endif %}
{% endmacro %}

Clear the cache and you are done.

In other templates we can add the class in same kind, hope this helps and comment below to any queries

Thanks

Add new comment

Restricted HTML