I want to generate the html elements and their classes to fit my styling of the main menu.

I'm confused by all the near-possibilities offered in the api documentation and by looking at core themes etc. and still can't quite see what I have to do, or where I have to do it.
I would be very grateful for clear pointers to:

  1. which php functions I have to write;
  2. which variables I need to add / update;
  3. in which files the functions will go.

Presumably, in MYTHEME.theme I will need a preprocess function of some sort to add to the attributes and item.attributes variables that links.html.twig uses to print the menu ul and the menu item li's.

[Update] So far I have discovered one way to place classes on the Main Menu's ul but ideally I would like to add a class to each submenu's ul indicating the depth of the sub-menu.
And I cannot locate any documentation or examples from which I can understand the generation / modifying of the main menu's individual link items.

This function goes into the file MYTHEME.theme.

function MYTHEME_preprocess_page(&$variables) {

  // Pass the main menu and secondary menu to the template as render arrays.
  if (!empty($variables['main_menu'])) {
    $variables['main_menu']['#attributes']['id'] = 'main-menu-links';
    $variables['main_menu']['#attributes']['class'] = array('links', 'clearfix', 'MYNEW_class');

  • You possibly want to wait for drupal.org/node/2256521 to land, that's a huge refactoring of the whole menu link system and it's likely that it will affect whatever you're currently doing.
    – Berdir
    Commented Jul 11, 2014 at 20:50
  • Thanks @Bedir. It looks like a mighty meaty refactoring! I'll keep the question open and aim to answer it when the new code hits an available alpha and I've had a chance to look at it.
    – iainH
    Commented Jul 12, 2014 at 15:43

3 Answers 3


Override the twig file for the main menu by saving menu--main.html.twig in your theme. Copy the original menu.html.twig from core/modules/system/templates/menu.html.twig, or core/themes/classy/templates/navigation/menu.html.twig if your theme extends Classy.

You can then modify the macro used to generate the menu output. To get a class on the <ul>'s to indicate level, you could do this.

{% if menu_level == 0 %}
  <ul{{ attributes.addClass('menu', 'menu-level--' ~ menu_level) }}>
{% else %}
  <ul class="menu {{ 'menu-level--' ~ menu_level }}">
{% endif %}

You could do similar manipulations to the <li>'s.

<li{{ item.attributes }}>

Use .addClass('class-name') to add more CSS classes.

<li{{ item.attributes.addClass('class-name') }}>

If you're a HTML/class name snob like myself you can also remove classes.

{{ item.attributes.removeClass('menu-item') }}
  • Nice answer @wesruv, I made some edits (pending) to try to answer the OP more directly on adding a class to indicate depth. Thanks!
    – star-szr
    Commented Jun 22, 2015 at 15:01
  • Thanks Cottser, I feel like I barely deserve credit for the much improved answer :)
    – wesruv
    Commented Aug 11, 2015 at 19:21

The way to get the main menu with items in all levels has been resolved in Drupal 8 - "Implement menu links as plugins", trying to build a menu tree

To add class to ul and li, we got 3 options:

A. Override menuTree service:

  1. create new MenulinkTree in your module MyprojectMenuLinkTree that extends core's MenuLinkTree
  2. Override MenulinkTree::build by adding MyprojectMenuLinkTree::build
  3. Update your level specifi class in logic like this https://gist.github.com/vijaycs85/308d2d0287a9ab1cd168
  4. ovrride defult menu.link_tree at sites/default/services.yml https://gist.github.com/vijaycs85/1179c6009c6932405f10

P.S: Not very sure, if you need to do these whole override for adding class.

B. Override just tree build on theme layer:

  1. Add the code in mytheme.theme file: https://gist.github.com/vijaycs85/61f2b2757f83aaa5260e

C. Using Drupal.behaviors.MYTHEME in JavaScript (from https://www.drupal.org/project/gratis)

  1. Create MYTHEME/script.js
  2. Add MYTHEME.behaviour.menuTree
  3. Update this code: https://gist.github.com/vijaycs85/346c14aedffcafe8945e
  • Thanks @vijaycs85. Since my question is essentially about theming, I think either of your options B and C will be sufficient for what I want. We temporarily abandoned our D8 project and until we eventually get back to it, it looks like you have provided the answer.
    – iainH
    Commented Aug 23, 2014 at 4:33
  • (I took this opportunity to re-read all 155 posts in drupal.org/node/2256521 to regain some confidence in continuing to invest in D8!)
    – iainH
    Commented Aug 23, 2014 at 4:40

In Drupal8 - if you want to use Macros & add levels etc to the menu, you need to pass the variables that you need to extend/include accordingly:

Base Template Screen Shot: enter image description here

Base Template Code:

{% import _self as menus %}

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

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

Child template code:

{% include "@drupalastic/navigation/menu_base.html.twig" with {
    item_add_class: ['my-custom-item-class', 'my-another-custom-item-class'],
    list_add_class: ['my-custom-list-class']

End Result: enter image description here

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.