I have a Drupal 7 installation that, for the purposes of this question, has just the core modules.

Using the Structure->Menus->Main Menu form, I added some items to the menu and, using a Mytheme_links__system_main_menu($main_menu) in the template file, dumped the menu items using print_r, just to check what is being passed through.

So far so good.

Now, in the Structure->Menus->Main Menu form, I add some child items beneath one of the top level items by dragging these items to the right one space under the parent. Then I clicked "Show as expanded" on the parent item and saved the configuration.

When I dump the $main_menu variable again, only the parent items are shown. The child items are never shown.

Can anyone help me understand how to ensure that the child items are rendered to the $main_menu array?

I don't really want to add pre-themed menu modules as there is a very simple but very specific layout that I need to achieve. Can the default Drupal menu module handle sub-menu items?

3 Answers 3


I think the problem your running into is that the menu system by default only returns the submenu on the path for that parent menu item.

When I've built bootstrap based themes (and wanted all the submenus expanded for dropdown menus) I've used hook_links___system_main_menu($vars)

It ends up looking like this (can't help but feel there must be an easier way!):

    * Implements hook_links__system_main_menu().
    * @param array $vars
    * @return string
    *  Themed HTML for bootstrap 3 ready main menu.
    function MYTHEME_links__system_main_menu($vars) {
    // Get the active trail
    $menu_active_trail = menu_get_active_trail();
    // Initialise our custom trail.
    $active_trail = array();

    // Get current path
    $dest = drupal_get_destination();
    if (is_string($dest['destination'])) {
      $paths = explode('/', $dest['destination']);
      // Loop through and add all active paths
      foreach ($paths as $path) {
        // Read previous element added to active trail (using array values
        // preserves original array).
        $safe = array_values($active_trail);
        $previous = array_pop($safe);
        if ($previous) {
          $active_trail[] = $previous . '/' . $path;
        // Or this is the first one
        else {
          $active_trail[] = $path;

    // UL classes
    $class = implode($vars['attributes']['class'], ' ');
    $html = '<ul class="' . $class . '"';
    // Check if there is an ID set (not if it's a dropdown sub-menu).
    if (isset($vars['attributes']['id'])) {
      $html .= ' id="' . $vars['attributes']['id'] . '"';
    $html .= '>';
    // Iterate links to build menu.
    foreach ($vars['links'] as $key => $link) {

      // Check this is a link not a property.
      if (is_numeric($key)) {
        $sub_menu = '';
        $li_class = array();
        $a_class = array();

        // Check if link is in active trail and add class.
        if (in_array($link['#original_link']['link_path'], $active_trail)) {
          $li_class[] = 'active-trail';
        if ($link['#original_link']['link_path'] == end($active_trail)) {
          $li_class[] = 'active';
        // Check if last element in list and see if LI contains actual link
        $link['#attributes']['class'][] = strtolower(str_replace(array('& ', ' '), array('', '-'), $link['#title']));
        $link_title = $link['#title'];
        // Open subscribe in a new window.
        if ($link_title == 'Subscribe') {
          $link['#localized_options']['attributes']['target'] = '_blank';
        if (isset($link['#localized_options']['attributes'])) {
          $link['#attributes'] = array_merge($link['#localized_options']['attributes'], $link['#attributes']);

        // Check if we have a submenu.
        if (!empty($link['#below'])) {
          // Check if lvl 1, if higher do other stuff
          if ($link['#original_link']['depth'] < 2) {
            $li_class[] = 'dropdown';
            $link_title .= '<b class="caret"></b>';
            $link['#attributes']['class'][] = 'dropdown-toggle';
            $link['#attributes']['data-toggle'] = 'dropdown';
          } else {
            $li_class[] = 'dropdown-submenu';
            $link_title .= '<b class="caret"></b>';
          // Theme submenu
          $sub_menu = theme('links__system_main_menu', array('links' => $link['#below'], 'attributes' => array('class' => array('dropdown-menu'))));
        // Build classes string
        $classes = '';
        if (!empty($li_class)) {
          $classes = ' class="' . implode($li_class, ' ') . '"';
        $html .= '<li' . $classes . '>' . l($link_title, $link['#href'], array('html' => 'true', 'attributes' => $link['#attributes'])) . $sub_menu . '</li>';
    $html .= '</ul>';
    return $html;

And finally add a new $main_menu_expanded for the page template in hook_preprocess_page

  // Get the entire main menu tree
  $main_menu_tree = menu_tree_all_data('main-menu');

  // Add the rendered output to the $main_menu_expanded variable
  $vars['main_menu_expanded'] = menu_tree_output($main_menu_tree);

A bit overkill for what you want as it adds all the bootstrap specific markup, but it's a solution.

  • Hi bloke_zero - thanks for your help. I ended up doing something very similar using MYTHEME_menu_link__MAIN_MENU and MYTHEME_menu_tree__MAIN_MENU. It seems like such a "fudge", I can't believe that Drupal doesn't do this sort of thing as standard, given how common multi-level menus are.
    – Ambulare
    Jan 27, 2015 at 10:52
  • Glad I could help. Maybe someone will come along and show us the Drupal way. I have looked in the past without avail.
    – bloke_zero
    Jan 27, 2015 at 11:44

It may be caused by the "Show as expanded" unchecked checkbox by default on your menu items edition page. Check 'admin/structure/menu/item/{mid}/edit'

By the way, devel's module dpm(object) function is the Drupal way of print_r / var_dump inspecting things.

  • thanks for your reply but I'm not sure I understand what you are suggesting. I have checked "Show as expanded" on the parent and have also now checked it on the child items just too see if it would help. Still no success.
    – Ambulare
    Jan 26, 2015 at 12:21
  • Sorry, Indeed I had not read your question correctly, it seems that the theme function you suggest does not handle child items. What I would have done for an advanced menu is to use menu_tree_all_data or such, then make my own theming function around it.
    – B2F
    Jan 26, 2015 at 12:45

The $main_menuvariable only contains first level menu items.

The default $main_menu variable in page.tpl.php is set to menu_main_menu(), which is a contextual view of the main menu tree.

To get your desired output, you need to get the entire menu tree using menu_tree_all_data() and menu_tree_output().

Have a look at this question. Show all nested menu links in Drupal 7

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.