Advanced Custom Menus in Drupal

Share this article

In the last article Creating Custom Menus in Drupal we saw how to respond to URLs,  how to create menus and sub-menus in Drupal and how to pass parameters that can be used to alter the output of the menu callback functions.

These features might meet your needs for the most of the modules you want to build on top of Drupal, but in addition to these, the Drupal menu system also provides us with more functionality such as passing default arguments to your callback function, using wild cards in menu urls, creating tabbed menus etc…

These features can be very useful and can help you structure and design  your module in an easier and more efficient way. In this article we are going to see some of these functionalities and learn how you could use them in your Drupal module.

Creating the module

Let’s start by creating a separate module called advancemenudemo. To do this create a folder advancemenudemo sitesallmodulescustom and add two files called as advancemenudemo.module and in it. Add the following code to the file:


name = advancemenudemo

description = A module to demo drupals menu system Part 2

package = Menu Demo Package

core = 7.x

files[] = advancemenudemo.module

If everything has gone correctly you should now see your module in the Drupal module list as seen below.


Passing Default Arguments in your Menu

In the last article we saw that the Drupal menu system passes any additional parameters in the URL as arguments. In many modules there is one common generic menu callback function, which responds to multiple menu items.

Some menu items might have an extra parameter and some might not. In order to use the same callback function the menu item in Drupal can define default arguments that will be passed to the callback function. And the callback can have its normal code which would expect certain arguments to process the output.

We add default arguments for our menu items as follows:


* Implementation of hook_menu().


function advancemenudemo_menu() {

  $menuitems['menudemo'] = array(

  'title' => 'My Menu',

  'page callback' => 'advancemenudemo_mymenu_page_callback',

  'page arguments' => array('Product', 'view'),

  'access callback' => TRUE,

  'type' => MENU_NORMAL_ITEM,


return $menuitems;


function advancemenudemo_mymenu_page_callback($firstparameter = '', $secondparameter ='') {

  $result = 'My Menu URL was hit';

  $result.='<br> The first parameter passed to the url is :<b>'.$firstparameter.'</b>';

  $result.='<br> The second parameter passed to the url is :<b>'.$secondparameter.'</b>';

  return $result;


In the above code we have specified the menu item parameter

'page arguments' => array('Product', 'view')

Two default arguments that are the product and view respectively will be passed to the callback function.

Now if you click on the menu item the output will be as follows


If you want to specify that the first argument passed to the callback function is a product and the second argument to the callback function should be fetched from the URL, you can update the menu item definition as follows:

$menuitems['menudemo'] = array(

  'title' => 'My Menu',

  'page callback' => 'advancemenudemo_mymenu_page_callback',

  'page arguments' => array('Product', 1),

  'access callback' => TRUE,

  'type' => MENU_NORMAL_ITEM,


Here you are specifying to the Drupal menu system that the first argument sent to the function advancemenudemo_mymenu_page_callback should be ‘Product’ and the second should be the first parameter in the URL after menudemo.

Now, if you go to the URL <your dupal installation path>/menudemo/edit the output will be as follows:


Note: – Drupal caches the hooks that a module implements, so you might have to go to Administration » Configuration » Development » Performance and do a clear cache to take into consideration the new changes in the hooks implemented by your module in Drupal 7.

Using Wild Cards in Menu

Sometimes you might want to respond to the URL in that you might not want to hardcode some parts of the URL, and that could be anything that then would be passed to your callback function.

Drupal lets you specify such wild cards in your URL. You have to use the % sign to signify a wild card. To define a URL with any word after menudemo in the URL you will have to specify the following menu item in your advancemenudemo_menu function:

$menuitems['menudemo/%'] = array(

  'title' => 'Wild Card Menu',

  'page callback' => 'advancemenudemo_mymenu_page_callback2',

  'page arguments' => array(1),

  'access callback' => TRUE,


function advancemenudemo_mymenu_page_callback2($firstparameter = '') {

  $result = 'My Menu URL with wildcard was hit';

  $result.='<br> The wildcard value parameter passed to the url is :<b>'.$firstparameter.'</b>';

  return $result;


In the above code we are specifying the menu item with a wildcard and the callback function is advancemenudemo_mymenu_page_callback2.The element at position 1, which is the wildcard string, will be passed as the argument to the callback function.

The function advancemenudemo_mymenu_page_callback2 just prints the value of the wildcard argument.

Now if you go to the URL <drual installation>/menudemo/view you will see the output as follows:


Loading objects with wild card

Sometimes you might not just want the wildcard value passed directly to the callback function, but might want to take that value and then do some processing or fetch some other information based on that argument, and then pass that information to the callback function.

The Drupal framework allows you to do that by specifying a load function that will be written after the wildcard in the menuitem definition. The value you specify in the definition an function with _load appended to it will be called with the value of the wildcard and the output of that function will be used as argument to the callback function.

To specify a load function with the wildcard the menu definition will be as follows:

$menuitems['menudemo/%/product/%product_id'] = array(

  'title' => 'Wild Card Menu With Load',

  'page callback' => 'advancemenudemo_mymenu_page_callback_with_load',

  'page arguments' => array(1,3),

  'access callback' => TRUE,


function product_id_load($id)


  //Here one can load a product from the database

  //or a file etc..

  $product = array();



    $product['name'] = 'Shirt';

    $product['quantity'] = '5';


  else if($id=='2')


    $product['name'] = 'Jeans';

    $product['quantity'] = '3';


  return $product;


function advancemenudemo_mymenu_page_callback_with_load($action, $product)


  $result = 'My Wild Card Menu With Load URL was hit';

  $result.='<br> The action parameter passed to the url is :<b>'.$action.'</b>';

  $result.='<br> The product parameter passed to the url is :<b>'.implode(" : ",$product).'</b>';

  return $result;


As the value in the second wildcard we have specified as %product_id the load function product_id_load will be called. In this argument we have just returned an array defining the product with that id. You could fetch the details in the load function from a database or from some file.

Now if you visit the url <drupal installation>/menudemo/view/product/2 you will see the output as follows:


Creating a Tabbed menu

The Drupal framework also lets you create tabbed menus. To define a tabbed menu first we have to define a normal menu item, then define a local default task, and then define different local tasks depending on the number of tabs you want. The menu definition to create three tabs is as follows:

$menuitems['menudemo/producttab'] = array(

  'type' => MENU_NORMAL_ITEM,

  'title' => 'Product Tab',

  'page callback' => 'advancemenudemo_mymenu_tabbed_callback',

  'page arguments' => array('Main Product Tab'),

  'access callback' => TRUE,


  $menuitems['menudemo/producttab/default'] = array(


  'title' => 'Default primary tab',

  'weight' => 1,


  $menuitems["menudemo/producttab/viewall"] = array(

  'type' => MENU_LOCAL_TASK,

  'title' => 'View All',

  'page callback' => 'advancemenudemo_mymenu_tabbed_callback',

  'page arguments' => array('View all Product Tab'),

  'access callback' => TRUE,

  'weight' => 2,


$menuitems["menudemo/producttab/editall"] = array(

  'type' => MENU_LOCAL_TASK,

  'title' => 'Edit All',

  'page callback' => 'advancemenudemo_mymenu_tabbed_callback',

  'page arguments' => array('Edit all Product Tab'),

  'access callback' => TRUE,

  'weight' => 3,


function advancemenudemo_mymenu_tabbed_callback($firstparameter = '') {

  $result = 'Tabbed menu';

  $result.='<br> The menu clicked is :<b>'.$firstparameter.'</b>';

  return $result;


In the above code we have created a Product Tab and two sub tabs: View all Product tab and Edit all Products tab. The callback function just displays whichever tab is clicked.

If we click on one of the tabs you should see the output as follows:



Drupal has a very strong and flexible menu system. It provides a rich set of features and does all the complex work behind the scenes to provide to  the module an easy interface to work with URLs and produce the content for a particular URL.

Drupal gives you ways with minimal code to create simple menu items through to more complex tabbed menus by adding a few lines of code to your Drupal module. Drupal does the complex work of parsing the URL and converts  it to a function call in your code by even fetching the arguments of passing default arguments in case the arguments are not passed.

Based on this infrastructure, your module can provide easy navigation to the user with minimum code.

Have fun adding amazing menus to your next Drupal module.

Abbas SuterwalaAbbas Suterwala
View Author

Abbas is a software engineer by profession and a passionate coder who lives every moment to the fullest. He loves open source projects and WordPress. When not chilling around with friends he's occupied with one of the following open source projects he's built: Choomantar, The Browser Counter WordPress plugin, and Google Buzz From Admin.

Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week
Loading form