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.
*.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
parentkey 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 aparentvalue 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_namekey. - 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.
parentnests links into a hierarchy by referencing another link's machine name;weightorders siblings under the same parent.- Omitting
menu_namedefaults a link into the Tools menu — always set it explicitly if you want a link inmainor 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.