Change record status: 
Introduced in branch: 

Drupal 8 introduces a new mechanism for registering routes, implementing Symfony2's Routing component and the Symfony2 CMF Routing component extension. As a result, the way routes are defined in Drupal 8 has changed.

Routes basically are the mappings between URL paths and their corresponding page and access callbacks. Formerly these mappings were provided in hook_menu(), which actually mingled menu item, local task, action and contextual link definitions with route mappings. In Drupal 8, these concepts are all separate (bigger version):

Path to page callback (controller) mappings along with access requirements are now defined on routes. Paths that do not need a menu item, local task, action or contextual link (Drupal 7 items that were defined as MENU_CALLBACK only) will not need anything but the route defined. If you need one or more menu items, local tasks, actions and contextual links associated with the route, you need to define those in the respective systems.

The actual mappings between route names and the page and access callbacks are defined in a file called [module_name].routing.yml. The new routing system allows for matching on more than just path; it supports matching by path, HTTP method, scheme (http vs. https), and host. HTTP Accept headers are in progress.

Key differences

  • Rather than defining routes in hook_menu(), they are now defined in module.routing.yml files, in YAML format.
  • Routes are identified by a machine name. By convention, route machine names should be module_name.sub_name.
  • Each route consists of a path e.g. /admin/content/book, including preceding slash.
  • Within the route, all wildcard parameters (formerly '%' or '%name') are named and displayed in the format of {node}. The names within curly brackets map to the argument names of the _content/_controller (see below) — e.g. {node} maps to $node in node_view($node).
  • "page callback" becomes, in most cases, when what's being returned is content that will be rendered inside an HTML layout with other blocks, a _content key pointing to the callable name.
  • The _controller or _content keys may not point to functions. They should reference a method of a class. That class will be instantiated when needed and the specified method called. Ex: _content: \Drupal\mymodule\Controller\MyClass::myMethod
  • The _controller or _content keys may also refer to registered services. To do so, specify the name of the service as registered in the service container, followed by a single colon and then the method to use. Ex: _controller: mymodule_controller:someMethod. The service will be instantiated as needed.
  • In the case of a callback that wants to return the entire response (not HTML, or even if HTML, we don't want additional blocks around it), a result that is not a fully-themed HTML page, _controller is used instead. (see '\Drupal\book\Controller\BookController::bookExport' and book.routing.yml)
  • Access checks may now be stacked, and multiple access checks may be run on a single route. See Route access control may be stacked for more details.
  • hook_menu_alter() is no more, and instead becomes a number of possible extension mechanisms which are enumerated in Replacements for hook_menu_alter().

Plenty examples and links to further information can be found at D7 to D8 upgrade tutorial: Convert hook_menu() to Drupal 8 APIs

Site builders, administrators, editors
Module developers
Updates Done (doc team, etc.)
Online documentation: 
Not done
Theming guide: 
Not done
Module developer documentation: 
Not done
Examples project: 
Not done
Coder Review: 
Not done
Coder Upgrade: 
Not done
Other updates done


vijaycs85’s picture

We can add custom implementation example with warning that it is not recommended as below:

Wildcard paths

Drupal 7

in modules/book/book.module:

function book_menu() {
   $items['book/export/%/%'] = array(
    'page callback' => 'book_export', 
    'page arguments' => array(2, 3), 
    'access arguments' => array('access printer-friendly version'), 
    'type' => MENU_CALLBACK, 
    'file' => '',
    $items['node/%node/outline'] = array(
    'title' => 'Outline', 
    'page callback' => 'book_outline', 
    'page arguments' => array(1), 
    'access callback' => '_book_outline_access', 
    'access arguments' => array(1), 
    'type' => MENU_LOCAL_TASK, 
    'weight' => 2, 
    'file' => '',
  return $items;

Drupal 8

in modules/book/book.routing.yml:

  pattern: '/book/export/{type}/{node}'
    # What's being returned is the entire response (not HTML, or even if HTML, we don't want additional blocks around it), so use _controller instead of _content.
    _controller: '\Drupal\book\BookController::export'
    _permission: 'access printer-friendly version'

  pattern: '/node/{node}/outline'
    _controller: '\Drupal\book\BookController::outline'
# Indicates that the book outline access access system should be invoked.
    _book_outline_access: TRUE

If you want to handle access logic on the controller, as you might need some values of the URL use '_access: TRUE' and throw exceptions on the controller

If you want to define custom wildcard, refer converters section


It is not recommended to define customer converters in D8 as most of them will be covered as entity or configentity.
There are two steps to create a custom converter (apart from entity and configentity)

  1. Implement ParamConverterInterface
  2. Register converter in a bundle

1. Implement ParamConverterInterface

File: core/modules/views/views_ui/lib/Drupal/views_ui/ParamConverter/ViewUIConverter.php

2. Register converter in a bundle

file: core/modules/views/views_ui/lib/Drupal/views_ui/ViewsUiBundle.php
dpi’s picture

It should be noted that adding a '_form' to .routing.yml will automatically load the file that contains the name of the class referenced by '_form'.

Australian Drupal Dev -

claudiu.cristea’s picture

Drupal 7

function image_menu() {
  $items = array();
  $directory_path = file_stream_wrapper_get_instance_by_scheme('public')->getDirectoryPath();
  $items[$directory_path . '/styles/%image_style'] = array(
    'title' => 'Generate image style',
    'page callback' => 'image_style_deliver',
    'page arguments' => array(count(explode('/', $directory_path)) + 1),
    'access callback' => TRUE,
    'type' => MENU_CALLBACK,
  return $items;

How to pass that $directory_path to the YML route?

Claudiu Cristea |

Xano’s picture

You will have to create a route subscriber. See \Drupal\block\Routing\RouteSubscriber and block.routing.yml for an example.

vacho’s picture

In drupal 8 dev this code not created menu item.
It is neccesary to implement module_name.menu_links.yml

  title: Books
  parent: system.admin_structure
  description: '...'
  route_name: book.admin

However this issue not exist in drupal 8 alpha 10 release

jastraat’s picture

I believe all _controller and _form values should start with a \
A number of the examples are missing the starting slash. For example:

_form: 'Drupal\system\Form\SiteInformationForm'

drakche’s picture

Is it possibe to change the documentation to reflect the proper way to implement the meny display for the latest version of Drupal 8? Since it's not working with implementation of hook_menu_default_links(), and there are no examples how to implement it, and the process is more trail and error.

I found that it's explained really bad, and also there are no examples for links nesting in the documentation, nor what combinations are possible.

I'm willing to help with the update of the docs if that's OK with you?

Gábor Hojtsy’s picture

Just updated removing the examples in favor of a much more up to date and more detailed tutorial. That should cover it.