Add SVG icons in Drupal menus

Adding SVG icons or button styles into Drupal menus can be difficult — especially if you’re trying to avoid hardcoded, fragile solutions. With the Menu Link Attributes module you can easily add custom HTML5 data attributes to individual menu links, then use a preprocessor to inject your SVG icons and button styles based on those attributes. This solution takes just a couple of minutes and should work on Drupal 8 / Drupal 9 installations.

Menu Link Attributes

Add the Menu Link Attributes module to your project via composer require drupal/menu_link_attributes, then enable the module with drush en menu_link_attributes or through the GUI on the Extend page. Once the module is enabled navigate to Structure > Menus and click the Available attributes tab. Here you’ll add YAML configuration that will describe which attribute options you’d like added to individual menu links.

Menu link attributes config
The menu link attributes configurations can be found at Structure > Menus > Available attributes (tab).
attributes:
  data-icon:
    label: Icon
    description: 'Replace the text of the menu link with an icon.'
    options:
      facebook: Facebook
      instagram: Instagram
      twitter: Twitter

Paste the above YAML into the configuration textarea and click Save to create a new Icon option for menu links using the custom data-icon attribute. With this option you can add attributes to your link like data-icon=”facebook” — or whatever you define in your config YAML. In our example we’ve defined facebook, instagram and twitter.

Menu link attributes settings
Menu link Attributes settings are now available on the edit screen for individual menu links using the configurations set above.

Next you’ll want to assign these data attributes to your Drupal menu items using the GUI. For this example I’ve created three new menu items in the Main navigation (machine name main) menu: Facebook, Instagram, and Twitter.

Sandbox theme homepage
A custom Sandbox theme, using Bartik as a base theme, on a fresh Drupal 8.2 install.

Create an SVG Sprite

This solution assumes you’re using a sprite of SVG icons and have included them somewhere in your templates. There are many ways to do this in Drupal, and covering them here would be outside the scope of this post. For simplicity, I’ve added the following sprite at the bottom of my page.html.twig file — you can follow suit to get this example working in your project. Notice that the value of the data-icon attribute in the menu links match the IDs of the symbol elements in the sprite.

<div style="display:none">
  <?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg"><symbol id="facebook" viewBox="0 0 24 24"><path d="M17.844 1.161v3.536h-2.103c-1.647 0-1.955.79-1.955 1.929v2.531h3.924l-.522 3.964h-3.402v10.165H9.688V13.121H6.273V9.157h3.415v-2.92C9.688 2.849 11.764 1 14.791 1c1.446 0 2.692.107 3.054.161h-.001z"/></symbol><symbol id="instagram" viewBox="0 0 24 24"><path d="M20.922 0A3.094 3.094 0 0 1 24 3.078v17.844A3.094 3.094 0 0 1 20.922 24H3.078A3.094 3.094 0 0 1 0 20.922V3.078A3.094 3.094 0 0 1 3.078 0h17.844zM4.875 10.156H2.672v10.125c0 .532.422.953.953.953h16.703a.947.947 0 0 0 .953-.953V10.156h-2.11c.204.64.313 1.344.313 2.047 0 4-3.343 7.235-7.468 7.235-4.11 0-7.454-3.235-7.454-7.235 0-.703.11-1.406.313-2.047zm7.14-2.875c-2.656 0-4.812 2.094-4.812 4.672s2.156 4.672 4.813 4.672c2.671 0 4.828-2.094 4.828-4.672s-2.157-4.672-4.828-4.672zm8.188-4.61h-2.719a1.08 1.08 0 0 0-1.078 1.079v2.578a1.08 1.08 0 0 0 1.078 1.078h2.72a1.08 1.08 0 0 0 1.077-1.078V3.75a1.08 1.08 0 0 0-1.078-1.078z"/></symbol><symbol id="twitter" viewBox="0 0 24 24"><path d="M22.696 5.464a9.294 9.294 0 0 1-2.17 2.237c.013.188.013.375.013.562 0 5.719-4.353 12.308-12.308 12.308-2.451 0-4.728-.71-6.643-1.942.348.04.683.054 1.045.054a8.667 8.667 0 0 0 5.371-1.848 4.334 4.334 0 0 1-4.045-3c.268.04.536.067.817.067.388 0 .777-.054 1.138-.147a4.327 4.327 0 0 1-3.469-4.246v-.054a4.367 4.367 0 0 0 1.955.549 4.325 4.325 0 0 1-1.929-3.603c0-.804.214-1.54.589-2.183a12.297 12.297 0 0 0 8.92 4.527 4.875 4.875 0 0 1-.107-.991 4.324 4.324 0 0 1 4.326-4.326c1.246 0 2.371.522 3.161 1.366a8.523 8.523 0 0 0 2.746-1.045 4.313 4.313 0 0 1-1.902 2.384 8.673 8.673 0 0 0 2.491-.67l.001.001z"/></symbol></svg>
</div>
SVG markup in page.html.twig
In my custom Sandbox theme, I've copied page.html.twig from the base theme and pasted the SVG code from above as the last line.

Inject your SVG markup with a preprocessor

In your mytheme.theme file (or sandbox.theme in my case), define the following hook to inject SVG markup in any menu item that has a data-icon attribute. As mentioned above, you’ll need to paste the SVG markup provided at the bottom of your page.html.twig file in order to see any icons actually populate.

/**
 * @file
 * Functions to support theming in the Sandbox theme.
 */
use Drupal\Component\Render\FormattableMarkup;
 
/**
 * Implements theme_preprocess_menu().
 */
function sandbox_preprocess_menu(&$variables, $hook) {    
  foreach ($variables['items'] as &$item) {
    $attributes = $item['url']->getOption('attributes');     
    if (isset($attributes['data-icon'])) {
      $title = new FormattableMarkup('
        <span class="visually-hidden">@title</span>
        <svg class="icon icon--@icon" viewbox="0 0 24 24">
          <use xlink:href="#@icon"></use>
        </svg>
      ', [
        '@title' => $item['title'],
        '@icon' => $attributes['data-icon'],
      ]);  
      $item['title'] = $title;
    }
  }
}

In this example using Sandbox, my custom sub-theme of Bartik, I added an svg { max-width: 25px; } CSS declaration to my theme's base styles so my menu icons aren’t the size of the whole viewport.

Example homepage with SVG icons in Drupal menu links.
SVG Icons in their proper place as Drupal menu items! I added a line of CSS (above) in my Sandbox theme to set the icon sizes.

Voila! Now you’ve got SVG icons in your Drupal menu links, and you can easily alter the data-icon link attributes with a point-and-click interface. Check back soon for a separate post on creating SVG sprites for your Drupal templates.

Drupal Drupal Planet Code