My twig file contains the following:

<li{{ item.attributes.addClass(classes) }}>
   {{ link(item.title, item.url) }}

Which outputs:

<li class="menu-item item--search">
    <a href="#search" title="Expand Search" data-drupal-link-system-path="&lt;front&gt;">Search</a>

But I really want to add a <span> tag around the text Search (for styling purposes and replacing the text with an icon image.

I tried replacing {{ link(item.title, item.url) }} with <a href="{{item.url}}"><span>{{item.title}}</span></a> but that loses valuable attributes provided by the link function (as well as the menu_link_attributes module). I'm not sure if I can do this in twig or I need to write some sort of function in my .theme file. Any help appreciated. Thank you.


You can try this code:

{% set tmp = '<span>'~item.title~'</span>' %}
{% set link_text %}{{ tmp|raw }}{% endset %}
{{ link(link_text, item.url) }}
  • For my purpose this was the easiest to implement and achieve the desired result - it also allows me to target specific menus and not apply it to all links. Aug 22 '18 at 17:08
  • 1
    Actually the elegant solution is to use {% set %} and {% endset %}, so you can write html between the tags.
    – Quan Lee
    Aug 22 '18 at 17:26

For adding markup to all links, you could implement hook_link_alter() in a custom module (in the following example named mymodule):

use Drupal\Component\Render\FormattableMarkup;

 * Implements hook_link_alter().
function mymodule_link_alter(&$variables) {
  if (!empty($variables['options']['span_added'])) {

  $variables['text'] = new FormattableMarkup('<span>@title</span>', [
    '@title' => $variables['text'],

  $variables['options']['span_added'] = TRUE;

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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