Last updated March 22, 2015. Created on September 19, 2013.
Edited by Mark_L6n, romainj, janusman, dpi. 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). If you want to have optional parameters at the end of the path, look at here
  • 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.

    Then provide further optional elements:

    • _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.

  • In case you want to have optional parameters at the end, so 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'
    • _role: A specific Drupal role, eg. 'administrator'. You can specify multiple ones via "," for OR and "+" for AND logic. Note that, since the roles available may differ between sites, it's recommended to use permission based access restriction when possible.
    • _access: TODO.
    • _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.
    • _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: TODO. See Access checking on routes for details.
  • options (optional): Additional options on how the route should interact. Common options are:
    • _access_mode: (ANY / ALL (default) If multiple requirements are given, specifies how they should be combined.
      If you choose ALL, all requirements access checkers have to ALLOW access, otherwise on ANY just one has to ALLOW. If any of the access checkers return KILL, access will be denied immediately.
    • _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 fully-themed HTML page 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'
    # Ensure *both* requirements below are checked to grant access to the route.
    _access_mode: 'ALL'
    _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.


nyl_auster’s picture

Hello, thanks for the doc.
As a dev, wondering why there are always underscores everywhere in yaml routing files ? (_content, _permission etc...)
Which part of this routing attributes are drupalism / symfony ? Are underscores used to show which parts of routing are "drupalisms" ?

drupalshrek’s picture

Thanks for the suggestion. I think though that the stackexchange article is just a huge mass of cut-and-paste and does not even attempt to answer the question which is asked.

Please fill in my Learning a foreign language questionnaire if you have a moment.

mbrett5062’s picture

I hope this helps, I will look at adding this into the article as well, if appropriate.

Found here.

The key concept is the controller which gets some parameters from the system and converts them to the response. In this example you have the parameter 'taxonomy_vocabulary'. Everything without an underscore is considered to be a parameter to the controller. If you want to specify a default value, you should put it into the defaults array. Properties that have nothing to do with the controller parameters are considered to be internal and have an underscore as prefix.