I have a module-generated menu (using yaml file) with 5 menu items. My requirements don't allow me to restrict menu item visibility by role because roles can be dynamically added. So I have to do this using permissions only.

What I've tried
(1) Restricting menu item access using route permissions - this shows a page forbidden but still allows the restricted menu item to be visible

Please see my actual files below:


  title: 'HR Manager'
  route_name: portal_hrm_core.hrm_link
  menu_name: hcm
  expanded: TRUE


portal_hrm_core. hrm_link:
path: '/admin/hrm' 
  _controller: '\Drupal... systemAdminMenuBlockPage'
  _title: 'HRM'
  _permission: 'access hrm_core_job+access_hrm_core_employee'

(2) Contrib modules - Menu Item Visibility and Menu Item Per Role Both of these are only role-based and can't support permissions

I know I can go really low-level and use something like hook_preprocess_menu but seems to heavy for such a simple requirement considering how many times that hook may be called.

So folks, please any further suggestions?


  • AFAIK point (1) is correct and should also hide menu items (using that method in my own projects). Menus are heavily cached, have you cleared cache after adding route permissions?
    – Hudri
    Jun 28, 2021 at 9:25
  • Hudri - your quick reply has strongly encouraged me! Thanks. Please I've updated the original question with my source files for your review. And I cleared the cache using both devel menu ui and drush cr. Still this menu item remains visible no matter the permissions, and I'm not logged in as admin.
    – ilechcod
    Jun 28, 2021 at 10:53
  • This is almost exactly the same I'm doing, I just don't have that expanded: true key in my files, don't know that key. I also have an additional options: _admin_route: TRUE in my routing.yml files, because I only need that for pages with admin theme.
    – Hudri
    Jun 28, 2021 at 11:07

1 Answer 1


Works now. Thanks Hudri for pointing me in the right direction. I re-checked your suggestion about clearing the cache. In the process I discovered that the following line was commented out in my lazyBuilder() method (these menu items are in my toolbar and are generated in code)

$manipulators = [
  ['callable' => 'menu.default_tree_manipulators:checkAccess']

The absence of those lines were causing my menu to ignore access checks, apparently. Uncommenting them out solved the problem. Thanks.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.