Providing module-defined menu links

Last updated on
15 September 2025

This documentation needs review. See "Help improve this page" in the sidebar.

Menu links are defined in static .yml files in modules. The naming of the .yml file is [MODULE MACHINE NAME].links.menu.yml.

Keys

Required

  • title - the text to be displayed for the menu item.

Optional

  • title_context - adds a context for translation. Useful if the value for title is something similar that could be accidentally translated when the title value is translated elsewhere in the system.
  • description - the description of the menu item.
  • route_name - used for internal links. The route name that the link points to. If this is not set, the url key must be set.
  • url - used for external links. This must be a fully qualified URL. If this is not set, the route_name key must be set.
  • parent - the plugin ID of the parent of the menu item.
  • weight - the relative weight of the menu item. Items are listed in the menu by order of weight (lowest to highest), and if the weight is the same, alphabetically. The default is 0 (zero) when not set.
  • enabled - whether or not the menu should be enabled by default. 0 (zero) is disabled, 1 (one) is enabled.
  • route_parameters - for routes with dynamic components (ie - the user ID in the entity.user.canonical route), this value must be provided to build the link.
  • menu_name - the machine name of the menu to which the menu item should be added. The default is the Tools menu (an optional administration menu).
  • options - a series of options to be used when rendering the menu link. See \Drupal\Core\Url::fromUri() for more details.

Example

example.admin: # The plugin ID
  title: 'Example settings'
  description: 'Manage example settings for your site'
  parent: system.admin_config_development
  route_name: example.admin
  weight: 100
  # The menu link is enabled by default, we can override it with:
  enabled: 0
  route_parameters: { key: 'value' }
  # If menu_name is omitted, the "Tools" menu will be used.
  menu_name: devel 
  options:
    query:
      uid: 1
    attributes:
      target: _blank
      class:
        - some-class
        - anotherclass

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 (or you can see the available routes by http://www.example.com/devel/routes URL if devel module is installed).

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.

Anchor links in .menu.yml

example.anchor:
  title: "Anchor example"
  description: "Scroll to top on the current page"
  url: "internal:#top"

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. For example, this creates a simplified version of the menu link at the top of the page:

$link_properties = ['title' => 'Example settings', 'route_name' => 'example.admin', 'menu_name' => 'devel', 'provider' => 'my_module'];
\Drupal::service('plugin.manager.menu.link')->addDefinition('example.admin', $link_properties);

Please note that, to add a new definition this way, you should specify a value for the provider key. This value is the name of the module, providing the menu link.

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.

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.

Help improve this page

Page status: Needs review

You can: