Adding menu links in .links.menu.ymlfor Drupal 8 , 9 , 10 , and 11

Last updated :  

A route without a menu link is a room with no door drawn on the floor plan — the room exists, you can walk into it if you already know the exact coordinates, but nobody stumbling around the building will ever find it. This lesson covers .links.menu.yml, the file that draws that door: it takes a route you've already defined and gives it a real, clickable entry in Drupal's navigation.

What you'll learn in this lesson

  • Why menu links live in their own YAML file, separate from routing and permissions
  • How a menu link points at a route by machine name, never by hardcoding a URL
  • How to nest links into parent/child hierarchies and control their order with weight
  • How to send a link to a different menu than the default

The source file

Path (relative to the Examples module's root): modules/menu_example/menu_example.links.menu.yml

# This file links a menu link with route_name.
#
# 'title' is the only required key.
# 'description' is shown either as a tooltip on the item or as the description
# of the option on the page in the admin UI.
# 'weight' is used to order the items. (Higher weights get placed towards the
# end of the menu among items on the same level.)
# 'route_name' is used to associate menu links to the corresponding route.
# 'parent' is used to put items into the menu hierarchy by referring to the
# parent menu link name.
#
examples.menu_example:
  title: 'Menu Example'
  description: 'Simplest possible menu type, and the parent menu entry for others'
  expanded: 1
  route_name: examples.menu_example

examples.menu_example.alternate_menu:
  title: 'Menu Example: Menu in alternate menu'
  # If menu_name is omitted, the "Tools" menu will be used.
  menu_name: 'main'
  route_name: examples.menu_example.alternate_menu

examples.menu_example.restricted:
  title: 'Restricted Example'
  parent: examples.menu_example
  expanded: 1
  route_name: examples.menu_example.restricted
  weight: 10

How it works, key by key

The top-level key is the menu link's own machine name

examples.menu_example isn't a route — it's the unique ID of this specific menu link, conventionally namespaced as module_name.something. You'll refer back to this exact string later if you want another link to nest under it as a child.

route_name — the only thing that actually makes the link work

This is the connector: it must exactly match a route name already declared in *.routing.yml. Drupal resolves the real URL from the route at render time, which means if you ever change that route's path, every menu link pointing at it updates automatically — you never hardcode a URL here.

title and description

Both are translatable strings the site builder never has to touch. title is the only required key and is what a visitor actually clicks. description shows up as a tooltip on the link itself, and as the descriptive text under the option when the same link appears in the Structure > Menus admin listing.

parent and expanded — building a hierarchy

examples.menu_example.restricted:
  title: 'Restricted Example'
  parent: examples.menu_example
  expanded: 1
  route_name: examples.menu_example.restricted
  weight: 10

parent nests this link underneath another link by referencing its machine name — here, underneath the top-level examples.menu_example entry. expanded: 1 tells the menu to render this link's own children inline by default, rather than requiring a visitor to click into it first. Without a parent key at all, a link sits at the top level of whichever menu it belongs to.

weight — controlling order among siblings

Links at the same level are sorted by weight, lowest first — a link with weight: -5 appears above one left at the default of 0, which appears above one set to weight: 10. It only affects ordering relative to siblings sharing the same parent, not the whole menu.

menu_name — choosing which menu a link lands in

examples.menu_example.alternate_menu:
  title: 'Menu Example: Menu in alternate menu'
  menu_name: 'main'
  route_name: examples.menu_example.alternate_menu

Omit menu_name and Drupal places your link in the admin "Tools" menu by default — genuinely easy to miss the first time, since nothing errors if you forget it, your link just quietly shows up somewhere you weren't expecting. Set it explicitly to main (the site's primary navigation) or any other menu's machine name to control exactly where a link appears.

Discovery is automatic, but caching isn't: Drupal scans every enabled module's *.links.menu.yml file and builds the menu link tree once, then caches it. If you add a brand-new link and it doesn't show up, that's almost always a stale menu-link cache, not a typo — run drush cr before you start debugging the YAML itself.

See it for yourself

Visit /examples/menu-example on your own DDEV site, then check the Tools admin menu for "Menu Example: Menu in alternate menu" — no wait, that one deliberately lands in the main menu instead, exactly because of the menu_name: 'main' override above. Compare the two and you'll see the difference immediately.

Quick check: you add a new link with a parent key pointing at a menu link machine name that doesn't exist yet — what happens? Drupal doesn't throw a fatal error; the orphaned link is silently placed at the top level of its menu instead, since a broken parent reference isn't a hard failure. It's a subtle bug precisely because nothing crashes — always double-check the exact spelling of a parent value against the entry it's supposed to reference.

Key takeaways

  • A menu link's top-level YAML key is its own unique machine name, not a route — routes are connected separately via the route_name key.
  • Never hardcode a URL in a menu link; always point at a route name so the link stays correct even if the route's path changes later.
  • parent nests links into a hierarchy by referencing another link's machine name; weight orders siblings under the same parent.
  • Omitting menu_name defaults a link into the Tools menu — always set it explicitly if you want a link in main or elsewhere.
  • Menu link discovery is cached; a missing new link is almost always solved by drush cr, not a YAML fix.

Coming up next

A top-level menu link is one way to navigate to a route. The next lesson covers a second, more specific mechanism — local tasks, the tabs you see across the top of a page like "View," "Edit," and "Delete" on a node — starting with .links.task.yml.