Menu Blocks & Placement
When to Use
Use this when you need a menu to actually appear on the page. A menu holds links but renders nothing on its own — you render it by placing a menu block in a theme region, then constraining which slice of the tree shows.
Decision
| You want to show... | Block settings |
|---|---|
| The whole top-level menu | level: 1, depth: 0 (unlimited) |
| Only the current section's children (contextual sidebar) | level: 2+, depth: 1, rely on active trail |
| A flat top bar, no sub-items | level: 1, depth: 1 |
| The full tree always expanded (mega-menu source) | expand_all_items: true |
Pattern
Every menu is exposed as a derivative of the system_menu_block block plugin — one derivative per menu (SystemMenuBlock, core/modules/system/src/Plugin/Block/SystemMenuBlock.php). Place it like any block, in config:
# block.block.olivero_main_menu.yml (real Olivero example)
plugin: 'system_menu_block:main' # deriver id = the menu id
region: primary_menu
settings:
level: 1 # tree level to start at (1 = top)
depth: 0 # how many levels to show (0 = unlimited)
expand_all_items: false # force-expand every item regardless of active trail
Three settings do all the shaping:
level— where the visible tree starts.level: 2renders only the children of the active top item — the standard "section sidebar" pattern.depth— how many levels deep to render fromlevel.depth: 1= one level only.expand_all_items— whenfalse(default), only the active trail's children expand; whentrue, the whole subtree renders (needed to feed a CSS/JS mega-menu).
The same menu can be placed multiple times with different settings (a full top bar + a contextual sidebar) — they are independent block instances over one menu.
Common Mistakes
- Wrong: Building a "current section" sidebar with
level: 1and hiding items in CSS → Right: Setlevel: 2so Drupal renders only the active section's children - Wrong: Expecting sub-items to show with
expand_all_items: falsewhen the user isn't on that branch → Right: Default expansion follows the active trail; setexpand_all_items: truefor always-open trees - Wrong: Creating a custom block to render a menu → Right: The core
system_menu_block:{id}derivative already exists for every menu - Wrong: Placing the block but seeing nothing → Right: Check the menu has links and the region exists in the active theme
See Also
- Menu System Fundamentals for where the links come from
- Menu Caching & Performance — a placed menu block carries the
route.menu_active_trailscache context - Related: Core Block Plugins and Block System Architecture