Last updated May 11, 2016. Created on September 19, 2013.
Edited by akalata, joachim, izus, olafkarsten. Log in to edit this page.

The simplest way to define routes is to create a module.routing.yml file (see the introductory example). Each route is defined as a machine name in the form of module_name.route_name (e.g. book.render) with the following properties:

  • path (required): The URL to the route, with a leading forward slash (e.g., path: '/book'). You can use dynamic properties by including them in curly braces. (e.g., path: '/node/{node}/outline'). These will be passed along as arguments via parameter converters to the controller/form (see below). Note that the first item of the path must not be dynamic. You can also define optional parameters at the end of your path.
  • defaults (required): Defines the default properties of a route. Provide one of the following to specify how the output is generated:
    • _controller: The value is a callable (typically classname::method)
      returning either
      • a renderable array. This will be rendered into HTML and used as the main content of a normal, themed Drupal page. See An introductory example to Drupal 8 routes and controllers.
      • returning a Symfony\Component\HttpFoundation\Response object. For non-HTML (e.g. XML feeds) or for only a "page fragment" of HTML. This will be sent directly, no theming or blocks will be added.

      Note that the value of _controller is not a path, but rather a namespaced class name using the PSR-4 standard for PHP namespace autoloading (see PSR-4 namespaces and autoloading in Drupal 8). The PSR-4 namespaced path name must map to a corresponding path structure:

      • namespace: \Drupal\[module name]\Controller\[ClassName]::[method],
        ex: _controller: '\Drupal\test_mod\Controller\TestModController::build'
      • path: [module name]/src/Controller/[ClassName].php,
        ex: test_mod/src/Controller/TestModController.php, containing class TestModController with method build() ('method' is an OO term for a function inside a class).

      Note that module names are lower-case with underscores, while class names are camel-case. See also An introductory example to Drupal 8 routes and controllers.

    • _form: This one expects a classname implementing Drupal\Core\Form\FormInterface. See form API in Drupal 8 for an example.
    • _entity_view: The value is entity_type.view_mode. Will find an entity in the path and render it in the given view mode. E.g. _entity_view: node.teaser will return the render array of the {node} in teaser mode. See entity view pages provided by the entity view controller
    • _entity_list: The value is entity_type. Provides a list of entities using the EntityListController of the respective entity. E.g. _entity_list: view_mode returns the render array of a list of view modes.
    • _entity_form: Similar to _form but will provide an edit form for an entity. Entity form handlers are defined in the entity metadata (annotation). For example, _entity_form: node.default will display the default node form. "node" in node.default refers to the entity ID, and "default" refers to the form controller key.
    • _title (optional): The page title for the route. May differ from the menu link title.
    • _title_context (optional): Additional context information for the title text passed along to t().
    • _title_callback (optional): A PHP callable (typically classname::method) returning the page title for the route.

    Finally, if any static arguments should be passed to the controller, provide those in the defaults array as well. Name them to match the arguments of your controller method. See below.

    Optional parameters: If you plan to include optional parameters at the end of your path, foo/bar/{baz} should also match foo/bar with baz being a specific value you should can set it in the defaults array.

  • requirements (required): Determines what conditions must be met in order to grant access to the route. Is an array of one or more of the following:
    • _permission: A permission string (e.g., _permission: 'access content'). You can specify multiple permissions by separating them with ',' (comma) for AND logic or '+' (plus) for OR logic. E.g., _permission: 'access content,access user profiles' Module-specific permission strings can be defined in my_module.permissions.yml. See for details.
    • _role: A specific Drupal role, eg. 'administrator'. You can specify multiple ones via "," for AND and "+" for OR logic. Note that, since the roles available may differ between sites, it's recommended to use permission based access restriction when possible.
    • _access: Set this to "'TRUE'" (with the single quotes) to have access granted to this route in all circumstances .
    • _entity_access: In the case where an entity is part of a route, can check a certain access level before granting access (e.g., _entity_access: 'node.view'). You can also specify how the entity should be validated (e.g., node: \d+). This is useful if you have a route /foo/{node} and /foo/bar where {node} is a node id. /foo/bar won't match /foo/{node} because bar isn't numeric (see book.routing.yml for an example).
    • _custom_access: TODO. See Access checking on routes for details.
    • _format: Use this to check the type of the request. For example, you can have _format: json and only match requests where the Accept header is json.
    • _content_type_format: Use this to check the content type of the request. For example, you can have _content_type_format: json and only match requests where the Content-type header is json. See API doc for ContentTypeHeaderMatcher.php to get an idea how it works.
    • _module_dependencies: Optionally use this key to specify one or more modules that are required to serve this path. You can combine module names with a + (plus) for an AND relationship or , (comma) for an OR relationship. For example, _module_dependencies: 'node + search' means both node and search are needed, _module_dependencies: 'node, search' means either node or search are needed. If your module already depends on other modules for its behaviour (via info.yml dependencies), there is of course no need for specifying the dependency here as well; however for optional dependencies, where routes are provided only if those optional dependent modules are also enabled, this is a useful option.
    • _csrf_token: should be used and set to 'TRUE' for any URLs that perform actions or operations that do not use a form callback. See Access checking on routes for details.
    • _method: Optionally use this key to restrict the route to specific http methods. Defaults to GET and POST. Expects a string. To restrict to more than one method concatenate with '|'. Example: 'GET|POST'. Will change with updating to symfony 3. Read change record.
  • options (optional): Additional options on how the route should interact. Common options are:
    • _access_mode: (ANY / ALL (default) For security reasons, this option has been removed. #2431281: Drop support for _access_mode routes and always assume ALL
    • _theme: TODO
    • parameters: If your parameter name from the path doesn't match any entity name you can translate the parameter to an entity here. And, as you can't have two parameters with the same name, let's say {user}, you can map the unmatched entity here.
      • second_user: The parameter name from the path. In this case it's {second_user}.
        • type: This is where we tell what entity to use. In this example we can just write entity:user, to map the parameter to the user entity.

Here's a more complex annotated example:


# Each route is defined by a machine name, in the form of module_name.route_name.
  # The path always starts with a leading forward-slash.
  path: '/book'
  # Defines the default properties of a route.
    # For page callbacks that return a render array use _controller.
    _controller: '\Drupal\book\Controller\BookController::bookRender'
  # Require a permission to access this route.
    _permission: 'access content'

  # This path takes dynamic arguments, which are enclosed in { }.
  path: '/book/export/{type}/{node}'
    # Because this route does not return HTML, use _controller.
    _controller: '\Drupal\book\Controller\BookController::bookExport'
    _permission: 'access printer-friendly version'
    # Ensure user has access to view the node passed in.
    _entity_access: 'node.view'

Passing arguments to controllers

All keys under the defaults section which do not start with an underscore will be passed in as arguments to the controller. Name your arguments appropriately for the arguments of the controller. For example a routing.yml file with the following:

  path: '/example' 
    _controller: '\Drupal\example\Controller\ExampleController::content' 
    custom_arg: 12
    _permission: 'access content' 

Will pass on $custom_arg to the controller, so your content method can take $custom_arg:

  // ...
  public function content(Request $request, $custom_arg) {
    // Now can use $custom_arg (which will get 12 here) and $request.

Combine this with dynamic routes to get true flexibility.

Looking for support? Visit the forums, or join #drupal-support in IRC.


dahousecat’s picture

This page (D7 to D8 upgrade tutorial: Convert hook_menu() and hook_menu_alter() to Drupal 8 APIs) refers to properties called base_route and parent_id however they are not mentioned in this document.

Should they be added to this page, or removed from the other article?

Getting pretty confused at the moment trying to figure all this stuff out!

ExTexan’s picture

In the code example for book.routing.yml, it says:

for book.render:

    # For page callbacks that return a render array use _controller.
    _controller: '\Drupal\book\Controller\BookController::bookRender'

for book.export:

    # Because this route does not return HTML, use _controller.
    _controller: '\Drupal\book\Controller\BookController::bookExport'

Both say "use _controller". If there was a difference early on in development, but it was changed at a later date, this text should be revised to not be so confusing. And not just here in this document, but in the code for the book module (if that code was actually copied from there).