Providing module-defined menu links

Last updated on
31 January 2017

Disclaimer: As of this writing, this part of Drupal 8 core is highly in flux and the information written here may not last very long.

Compared to Drupal 7, hook_menu has been removed from Drupal 8. Routing is now handled by the new routing system and menu links are now defined in static .yml files. The naming of the .yml file should be module_name.links.menu.yml. To provide a configuration menu item under the development settings, you can do the following:

example.admin:
  title: 'Example settings'
  description: 'Manage example settings for your site'
  parent: system.admin_config_development
  route_name: example.admin
  weight: 100
  route_parameters: { key: 'value' }
  #If menu_name is omitted, the "Tools" menu will be used.
  menu_name: devel 
  options: 
    attributes:
      target: _blank

Only the title key is required. This example defines a local menu link so it uses a route_name to tie the menu link to a route. (External pointing menu links would use an url value). The description is shown either as a tooltip on the item or in the admin UI as the description of the option on the page. The weight is used to order the items (higher weights get placed towards the end of the menu among items on the same level). Finally, the item can be put into the menu hierarchy by referring to the parent menu link name. See further possible keys on the hook_menu_links_discovered_alter() documentation.

Determining the parent menu_link name can be a little tricky at first. If you know the path of the parent menu item, you would need to search for it in all available *.routing.yml files (most text editors and IDEs should allow you to search all files in a project) and find the route name for that path. Then, you would need to search for the route name in all available *.links.menu.yml files. The menu_link whose route_name matches is your link. Alternatively, if you know which module is defining the parent menu link (or one of the parent menu link's children), you can go straight to that module and perform your search.

Finally, you can provide string context for the menu link title in a title_context key, so if the link text is ambiguous (such as 'Extend', 'May', etc.) the string context helps translators pick the right translation. This is later passed on to t().

The route name for paths created by Views pages is in the format view.name.display_id.

Altering menu links and adding menu links dynamically

Statically defined menu items may be altered using hook_menu_links_discovered_alter() (but note there is no hook_menu_link_discovered(), the menu links are built from the .yml files as explained above). This hook may also be used to add new dynamic menu items.

More often, you may want to use a deriver class to add new menu link plugins. For example, Views provided menu links are added in views.links.menu.yml, which specifies the deriver class \Drupal\views\Plugin\Derivative\ViewsMenuLink.

Finally, you can use the plugin manager directly to add, update, and remove definitions.

A little change history...

The original replacement for menu links via hook_menu() was proposed to be called hook_default_menu_links() #2047633: Move definition of menu links to hook_menu_link_defaults(), decouple key name from path, and make 'parent' explicit, but that eventually changed to the method above hook_menu_link_defaults() moved to *.links.menu.yml files. hook_menu_link_defaults_alter() does *currently* still exist, however.

Routing

Refer to the routing system documentation on how to define the 'example.admin' route. The routing system will associate the path to a controller, while the menu system will make the item appear in the administration section.