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('http://www.google.com'));
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:
$link->toRenderable();
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}
Defaults
_controller \Drupal\node\Controller\NodeViewController::view
_title_callback \Drupal\node\Controller\NodeViewController::title
Requirements
node \d+
_entity_access node.view
_method GET|POST
Options
compiler_class \Drupal\Core\Routing\RouteCompiler
parameters node:
type: 'entity:node'
converter: paramconverter.entity
_route_filters method_filter
content_type_header_matcher
_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': node.id( ) }}"> {{ '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()]);
Comments
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.
2017 May 05
Dave Hansen-Lange
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
Lars
Typo
Hi, you have a typo in the twig-template link:
<a href="{{url('entity.node.canonical', {'node': node.id( ) }}"> {{ 'This is a link'|t }} </a>
should be:
<a href="{{url('entity.node.canonical', {'node': node.id} ) }}"> {{ 'This is a link'|t }}
I like the article btw. I referenced it from my own blog.
2019 June 06
Blaine
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
Steven
2021 November 12
Darkhan
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