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.
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.
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.
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>
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.
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.