Skip to main content

Creating Links in Code for Drupal 8

There are plenty of ways—all right, too many ways—to create links when using modern Drupal (Drupal 8 and above, so including so far Drupal 9, Drupal 10, and Drupal 11).  We will share some of those ways in this regularly-updated post.  (Latest update: 2024 April 22)

The easiest way to create internal links is using Link::createFromRoute

And it is used like this:

use Drupal\Core\Link;
  $link = Link::createFromRoute('This is a link', 'entity.node.canonical', ['node' => 1]);

Using the Url object gives you more flexibility to create links, for instance, we can do the same as Link::createFromRoute method using the Url object like this:

  use Drupal\Core\Link;
  use Drupal\Core\Url;
  $link = Link::fromTextAndUrl('This is a link', Url::fromRoute('entity.node.canonical', ['node' => 1]));

And actually Link::fromTextAndUrl is what Drupal recommends instead of using the deprecated l() method. Passing the Url object to the link object gives you great flexibility to create links, here are some examples:

Internal links which have no routes:

$link = Link::fromTextAndUrl('This is a link', Url::fromUri('base:robots.txt'));

External links:

$link = Link::fromTextAndUrl('This is a link', Url::fromUri(''));

links with only the fragment (without url) :

$link = Link::fromTextAndUrl('This is a link', Url::fromUri('internal:#fragment'));

Using the data provided by a user:

$link = Link::fromTextAndUrl('This is a link', Url::fromUserInput('/node/1');

The param passed to fromUserInput must start with /,#,? or it will throw an exception.

Linking entities.

$link = Link::fromTextAndUrl('This is a link', Url::fromUri('entity:node/1'));

Entities are a special case, and there are more ways to link them:

$node = Node::load(1);
$link = $node->toLink();
$link->setText('This is a link');

And even using the route:

$link = Link::fromTextAndUrl('This is a link', Url::fromRoute('entity.node.canonical', ['node' => 1]));

Drupal usually expects a render array if you are going to print the link, so the Link object has a method for that:


which will return an array.

Linking files.

Files are entities also in Drupal but a special kind.  You usually do not want to link to the entity but directly to the uploaded file.  To do that, do not use $file->getFileUri() but rather instead use createFileUrl() which will directly give you a complete string version of the URI:

$url = $file->createFileUrl();

This URL can then be used in making a link with regular HTML or the External links approach covered above.

Credit to godotislate in Drupal Slack support channel for this tip.

Final tips:

Searching a route using Drupal Console

The easiest way to find the route of a specific path is using Drupal Console, with the following command.

$ drupal router:debug | grep -i "\/node"

That will return something like:

entity.node.canonical                                 /node/{node}
 entity.node.delete_form                               /node/{node}/delete
 entity.node.edit_form                                 /node/{node}/edit
 entity.node.preview                                   /node/preview/{node_preview}/{view_mode_id}
 entity.node.revision                                  /node/{node}/revisions/{node_revision}/view
 entity.node.version_history                           /node/{node}/revisions
 node.add                                              /node/add/{node_type}
 node.add_page                                         /node/add
 node.multiple_delete_confirm                          /admin/content/node/delete
 node.revision_delete_confirm                          /node/{node}/revisions/{node_revision}/delete
 node.revision_revert_confirm                          /node/{node}/revisions/{node_revision}/revert
 node.revision_revert_translation_confirm              /node/{node}/revisions/{node_revision}/revert/{langcode}
 search.help_node_search                               /search/node/help
 search.view_node_search                               /search/node
 view.frontpage.page_1                                 /node

Listing all the possible routes with that word, we can choose one and do:

drupal router:debug entity.node.canonical

And that will display more information about a specific route:

 Route             entity.node.canonical
 Path              /node/{node}
  _controller      \Drupal\node\Controller\NodeViewController::view
  _title_callback  \Drupal\node\Controller\NodeViewController::title
  node             \d+
  _entity_access   node.view
  _method          GET|POST
  compiler_class   \Drupal\Core\Routing\RouteCompiler
  parameters       node:
                     type: 'entity:node'
                     converter: paramconverter.entity

  _route_filters   method_filter

  _route_enhancers route_enhancer.param_conversion

  _access_checks   access_check.entity

So in this way we can search the route without the needing to search in all the *.routing.yml files and in this example the route is entity.node.canonical and the param expected is node.

Print links directly within a twig template

It is also possible to print links directly on the twig template with the following syntax:

<a href="{{url('entity.node.canonical', {'node': ) }}"> {{ 'This is a link'|t }} </a>

Add links inside a t() method.

If you want to add a link inside the t() you can do what Drupal suggests on Dynamic or static links and HTML in translatable strings:

use Drupal\Core\Url;
$url = Url::fromRoute('entity.node.canonical', ['node' => 1]);
$this->t('You can click this <a href=="@link">Link</a>', ['@link' => $url->toString()]);


2017 April 20
Pedro Rocha


Your post is really useful,

Your post is really useful, but D8 requiring us to have such a post is an example of how complex it is to do such a simple thing as a link.

I disagree, with D7 we had to

I disagree, with D7 we had to remember l(), url(), theme_link(), '#theme' => 'link', and more. With D8 you just have to remember Drupal\Core\Link and Drupal\Core\URL. You can get to all the rest via autocomplete in your IDE.

2018 April 18



Hi, you have a typo in the twig-template link:
<a href="{{url('entity.node.canonical', {'node': ) }}"> {{ 'This is a link'|t }} </a>
should be:
<a href="{{url('entity.node.canonical', {'node':} ) }}"> {{ 'This is a link'|t }}
I like the article btw. I referenced it from my own blog.

2019 June 06


Thanks for this very useful…

Thanks for this very useful article. 

Best resource I could find and it's now over 2 years old.

2020 August 16
Eric J Gruber


Just dropping a line to say…

Just dropping a line to say that I found this post helpful today.

To some, I realize this might be more complex than just making a simple href link in the php code, but when you get down to it, it's way more simpler to build a link using the Link and Url classes, assigning them to a variable, and then mixing them into the code. No headaches having to deal with escaped quotes and the system builds the link for you. It's a win.

2021 November 02
Steven Schulz


Thanks for explaining how…

Thanks for explaining how links work in Drupal.

Kind regards from Hamburg


2021 November 12


Hi! Can you show an example…

Hi! Can you show an example of how to create Url with arguments?

I want to create a link to a view with contextual filter, like /news/2021, /news/2020, etc.

Add new comment

The content of this field is kept private and will not be shown publicly.


The comment language code.
CAPTCHA Please help us focus on people and not spambots by answering this question.