I have been asked to add target="_blank" to the links that are produced when a user uploads a file on a Drupal 8 site.

The field type is "File". Multiple uploads are allowed.

Twig debug shows me that the field is being produced by the core template "file-link.html.twig". The entire uncommented contents of that template are:

<span{{ attributes }}>{{ link }}</span>
{% if file_size %}
  {# @todo remove class before Drupal 9.0.0 #}
  <span class="file-size">({{ file_size }})</span>
{% endif %}

I brought that template into my custom theme and tried to add the attribute to the link. I thought I'd be editing the {{ link }} part of the template, changing it to attach attributes. I did variations of this:

{{ link(item.title, item.url, attributes.setAttribute('target','_blank')) }}

Every syntax I tried produced a fatal PHP error. Thinking I was doing the attribute syntax wrong, I tested by simply trying to add a single class.

{{ link(item.title, item.url, { 'class': ['bananas'] }) }}

That was still a white screen of death, so I guess I can't use that syntax on the {{ link }} field in my file-link template.

I've been googling my eyeballs out of my head. I found this cool idea:

{% for item in items %}
  {{ item.content|merge({'#attributes': {'class': ['button', 'button--arrow', 'button--centered']}}) }}
{% endfor %}

so I adapted it for my field.html.twig template like so, changing this part:

{% for item in items %}
    <div{{ item.attributes }}>{{ item.content }}</div>
{% endfor %}

to this:

{% for item in items %}
      <div{{ item.attributes }}>{{ item.content|merge({'#attributes': {'target': ['_blank']}}) }}</div>
{% endfor %}

And while at least the page loaded and didn't have a fatal error, the target attribute did not appear on the link.

Does anyone have any advice for me getting an attribute onto a file upload link? Almost everything I've found has to do with the link field, not the file upload field. Thanks for your time.

1 Answer 1



<span{{ attributes }}>{{ link|merge({'#attributes': {'target': '_blank'}}) }}</span>
{% if file_size %}
  {# @todo remove class before Drupal 9.0.0 #}
  <span class="file-size">({{ file_size }})</span>
{% endif %}

Alternatively, you can also solve this via preprocess.


 * Implements hook_preprocess_HOOK().
function mytheme_preprocess_file_link(&$variables) {
  $variables['link']['#url']->setOption('attributes', ['target' => '_blank']);
  • That worked perfectly. I tested both methods and either one works. Thank you and if I could give two checks I would! For you and anyone who is interested in this topic, which of these two methods do you prefer? My pure guess only surface logic: the twig version is slightly more efficient because it only gets run if the template is used to build the page, whereas the preprocessing is run on every page build. I don't believe it makes a human noticeable difference, but I have to pick one, so trying to use logic.
    – LeraA
    Oct 20, 2020 at 12:54
  • FWIW, I'm going with the preprocess method for now, since it means I have one less custom template. But if I was already using that custom template for something else, I think I would use that method instead.
    – LeraA
    Oct 20, 2020 at 14:34
  • To me it would come down to. Do I want to give a front-end developer, which I don't expect to know PHP, the ability to potentially accidentally, or on purpose, be able to remove the open in a new tab feature? If the answer is yes, template file. If the answer is no, this should never be removed, preprocess.
    – No Sssweat
    Oct 20, 2020 at 21:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.