Creating a page with tabs in Drupal

If you are developing a module for Drupal you might need to use tabs to simplify a bit a screen which has too much functionality. Typically a settings page that might need too many options to comfortably fit on a single screen. Since I had to search and experiment a bit to find out how to do it I decided to write a short post with my findings. In this article I assume you have a basic understanding on writing Drupal modules.

Basically you define what content goes in what tab in your modules hook_menu() function defining menu items which are called MENU_LOCAL_TASK. Suppose I want a page which has two tabs: Company and Mail as seen on the screen shot. This is the corresponding hook_menu() code:

/**
 * Implementation of hook_menu()
 */

function my_module_menu() {

  $items = array();

  // definition for main page
  $items[‘settings’] =
    array(
      ‘title’ => t(‘Settings’),
      ‘description’ => t(‘Settings for Ticket System’),
      ‘page callback’ => ‘my_module_settings_root’,
      ‘access callback’ => true,
    );
  // definition for default tab.
  $items[‘settings/company’] =
    array(
      ‘title’ => t(‘Company’),
      ‘description’ => t(‘Settings for Ticket System’),
      ‘type’ => MENU_DEFAULT_LOCAL_TASK,
      ‘weight’ => 0,
    );
  // definition for additional tab.
  $items[‘settings/mail’] =
    array(
      ‘title’ => t(‘Mail’),
      ‘description’ => t(‘Settings for Ticket System’),
      ‘access callback’ => true,
      ‘page callback’ => ‘my_module_settings_mail’,
      ‘type’ => MENU_LOCAL_TASK,
      ‘weight’ => 2,
    );

  return $items;
}

function my_module_settings_root() {
  return ‘page root’;
}

function my_module_settings_mail() {
  return ‘page mail’;
}

Here are the main highlights of how this works:

  • The first item should be a standard menu item and its route should be shared with the other tabs. In this case, the route is ‘settings’.
  • Each additional tab should be of type MENU_LOCAL_TASK.
  • You need at least two tabs. If you only have one child route then you will not see a tab.
  • If you add more than one MENU_LOCAL_TASK, you must single out one as required by setting its type to MENU_DEFAULT_LOCAL_TASK. The default local task doesn’t need a ‘page callback’ since it is handled by the root item.
  • Additional tabs should be of type MENU_LOCAL_TASK and should have a page callback function assigned to them.
  • The ‘title’ of the standard item appears a the top of the tab bar. The title of the MENU_LOCAL_TASK and MENU_DEFAULT_LOCAL_TASK are used as the titles on the individual tabs.
  • You can change the order of the tabs by adding a ‘weight’ item to each menu
    item.
  • You need to specify an ‘access callback’ item and have permissions to view the corresponding tab.

Finally, remember that each time you change an item in hook_menu() you need to flush your cache so that the items are re-read. This can be achived by going to admin->settings->performance and hitting clear cache or by using drush and doing ‘drush cache clear’.