Creating Links Within Twig Templates Using path() and url()

Drupal 8 comes packed with loads of great new features, APIs and developer tools. There are sweeping changes aplenty. Not the least of which is a brand new templating system called Twig. Twig is the bee's knees and a welcome improvement over the much maligned PHPTemplate. However, many front-end developers who've grown accustomed to the templating ways of old might feel a bit lost as they enter the strange, new world of Drupal 8: Twig and render arrays. Furthermore, the Drupal community is still learning, documentation is still being written, etc.

As we approach the completion of our first official Drupal 8 project here at Chromatic, I thought it would be helpful to start sharing a bit of what I've learned along the way, starting with some examples on linking from within Twig templates.

If you want to just see how the hell you link to things from within Twig templates, skip these next bits on context.

Some Context

In Drupal 7 and versions prior, we used paths to define destinations for our content, APIs, etc. In Drupal 8, these are abstracted into routes. Where in Drupal 7 you would define a custom page via hook_menu(), like so:

<?php
function chromatic_conact_menu() {
  $items['contact'] = array(
    'title' => 'Chromatic Contact',
    'page callback' => 'contact_page',
    'access arguments' => array('access content'),
    'type' => MENU_SUGGESTED_ITEM,
  );

  return $items;
}
?>

In Drupal 8, you define this in a special routing file that follows this naming convention: module_name.routing.yml. Here's a similar example, where chromatic_contact_contact is our route name and contact is the internal path where it can be accessed.

chromatic_contact_contact:
  path: 'contact'
  defaults:
    _form: '\Drupal\chromatic_contact\Form\ChromaticContactForm'
    _title: 'Contact Us!'
  requirements:
    _permission: 'access content'

Here's how Drupal 8 sets up the route for node type creation:

node.type_add:
  path: '/admin/structure/types/add'
  defaults:
    _entity_form: 'node_type.add'
    _title: 'Add content type'
  requirements:
    _permission: 'administer content types'

Got it. Routes are the new hotness in D8. Now why does this matter to me? I'm a front-ender.

Glad you asked. Routes matter because if you want to generate URLs to custom pages from your templates and you want to do it properly, you need to understand routes. The old methods of using paths are "dead in Drupal 8".

The url() and path() functions are how we handle this type of linking in D8. These functions don't expect an internal path like you're probably familiar with in prior versions of Drupal. Instead, they expect proper system routes. The type of thing you now see in module_name.routing.yml.

So what's the difference?

  • path() - Generates a [relative] URL path given a route name and parameters.
  • url() - Generates an absolute URL given a route name and parameters.

Examples

// Link to the default frontpage content listing view:
<a href="{{ path('view.frontpage') }}">{{ 'View all content'|t }}</a>

// Link to a specific node page:
<a href="{{ path('entity.node.canonical', {'node': node.id}) }}">{{ 'Read more'|t }}</a>

// Link to a specific user profile page:
<a href="{{ path('entity.user.canonical', {'user': user.id}) }}">{{ 'View user profile'|t }}</a>

// Link to a view, and throw in some additional query string parameters:
<a href="{{ path('view.articles.page_1', {'page': 2}) }}">{{ 'Go to page 2'|t }}</a>

// Link to a view and pass in some arguments to the view:
<a href="{{ path('view.recent_articles_by_author.page_1', {'arg_0': user.field_display_name.value|drupal_escape }) }}">{{ 'See all the articles written by'|t }} {{ user.field_display_name.value }}</a>

The source code for the path() and url() Twig extensions can be found here within Drupal 8 core: /core/lib/Drupal/Core/Template/TwigExtension.php.

If you're curious about how all of this came to pass, here's the D.O issue on which the work occurred: https://www.drupal.org/node/2073811. It's long. Really long. The tldr; is basically a lot of back and forth over how routes are a barrier to entry for beginners and that there should still be an easy way to use paths. I honestly see both sides, but in the end, routes won out in the name of consistency and compatibility. The original patch contained a urlFromPath() function but it has since been removed. :-/. Beginner or not, you should now understand how to generate URLs within your templates. Now go! Commence Twigging!

One more thing!

The Drupal.org documentation page that explained these Twig functions (and others) was marked as incomplete, so I took this opportunity to finish it up. If you have other examples to share, please do so there!

Drupal Drupal 8 Theming Twig
Chris Free Headshot

Chris splits his time between running Chromatic and managing/consulting on enterprise Drupal projects. In his personal life, Chris is a dad to Anjali, a CrossFitter, a craft beer drinker, a BMW enthusiast and a Chicago sports fanatic.