SVG Sprites: Embed flexible, stylable, fast-loading SVG icons

Implementing iconography with SVG sprites is a powerful alternative to using standard image icons in formats like PNG and JPG. SVGs are XML encoded vector based graphics. They appear just like other image formats would on your website — but due to being encoded in a markup language (XML) they load much faster, are beautifully scalable, and are CSS styleable in some surprising and flexible ways.

SVG icons can be styled via CSS to nearly the same degree as an HTML element. You’ll be able to control the color, size and hover states of your icons — and even apply some visual effects like blurs — all from the comfort of your existing stylesheets.

This post will cover acquiring your SVG icons, combining them into a sprite, as well as placing and styling the icons with HTML and CSS.

Get your SVGs together

Where do SVGs come from? SVGs can be directly exported from vector graphics editors or design programs, applications like Adobe Illustrator, Photoshop, and Inkscape among others. You can also download prefab SVG icons from a host of sites like Shutterstock or Flaticon — often with a premium membership for the best selection. You can also convert your existing icons or images into SVG format with some of the applications listed above, or with online conversion services like Convertio.

Once you’ve downloaded or exported your SVG icons, you can open them in your favorite editor to see their XML markup. SVG markup is the cornerstone of its flexibility. You’ll be able to target the SVG image or its path elements with CSS, and bring your icons in line with the rest of your design with a few additions to your styles.

For this example, I’ll use a few contact icons for mail, telephone, and location downloaded from Flaticon. Here’s one for mail — I removed all the newlines so the XML presents in a single row of markup:

<?xml version="1.0" encoding="iso-8859-1"?><!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  --><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 241.061 241.061" style="enable-background:new 0 0 241.061 241.061;" xml:space="preserve"><path d="M198.602,70.402l-78.063,68.789l-78.08-68.79c-3.109-2.739-7.848-2.438-10.586,0.669c-2.737,3.108-2.439,7.847,0.67,10.586l83.039,73.159c1.417,1.248,3.188,1.872,4.958,1.872s3.542-0.624,4.959-1.873l83.022-73.159c3.107-2.738,3.406-7.478,0.668-10.586C206.449,67.964,201.711,67.664,198.602,70.402z"/><path d="M218.561,38.529H22.5c-12.406,0-22.5,10.093-22.5,22.5v119.002c0,12.407,10.094,22.5,22.5,22.5h196.061c12.406,0,22.5-10.093,22.5-22.5V61.029C241.061,48.623,230.967,38.529,218.561,38.529z M226.061,180.031c0,4.135-3.364,7.5-7.5,7.5H22.5c-4.136,0-7.5-3.365-7.5-7.5V61.029c0-4.135,3.364-7.5,7.5-7.5h196.061c4.136,0,7.5,3.365,7.5,7.5V180.031z"/></svg>

As previously mentioned you can drop this XML right into your markup where you’d normally place an image — but it feels a little messy to include 1000 characters of XML code every time you display an icon. That’s where sprites come in.

Creating your SVG sprite

For larger projects there are lots of ways you can and should automate this process, but that’s a post for a different time. The gist of putting an SVG sprite together is pretty straightforward. From each of your individual SVG files, copy the markup to a separate editor screen then strip away the XML, DOCTYPE and SVG tags. Take note of the viewBox declaration in the SVG tag — you’ll want to copy that in a moment. You should be left with some path declarations and perhaps a few g declarations. Here’s our mail icon stripped down:

<path d="M198.602,70.402l-78.063,68.789l-78.08-68.79c-3.109-2.739-7.848-2.438-10.586,0.669c-2.737,3.108-2.439,7.847,0.67,10.586l83.039,73.159c1.417,1.248,3.188,1.872,4.958,1.872s3.542-0.624,4.959-1.873l83.022-73.159c3.107-2.738,3.406-7.478,0.668-10.586C206.449,67.964,201.711,67.664,198.602,70.402z"/><path d="M218.561,38.529H22.5c-12.406,0-22.5,10.093-22.5,22.5v119.002c0,12.407,10.094,22.5,22.5,22.5h196.061c12.406,0,22.5-10.093,22.5-22.5V61.029C241.061,48.623,230.967,38.529,218.561,38.529z M226.061,180.031c0,4.135-3.364,7.5-7.5,7.5H22.5c-4.136,0-7.5-3.365-7.5-7.5V61.029c0-4.135,3.364-7.5,7.5-7.5h196.061c4.136,0,7.5,3.365,7.5,7.5V180.031z"/>

Next you’ll wrap your SVG in a symbol declaration where you’ll give it an ID and re-establish its viewBox. Now repeat the process with each of your SVG icons, until you end up with a couple of symbol declarations wrapping up all of the innards of your various SVG files. Below I’ve added telephone and location icons:

<symbol id="icon--mail" viewBox="0 0 241.061 241.061">
  <path d="M198.602,70.402l-78.063,68.789l-78.08-68.79c-3.109-2.739-7.848-2.438-10.586,0.669c-2.737,3.108-2.439,7.847,0.67,10.586l83.039,73.159c1.417,1.248,3.188,1.872,4.958,1.872s3.542-0.624,4.959-1.873l83.022-73.159c3.107-2.738,3.406-7.478,0.668-10.586C206.449,67.964,201.711,67.664,198.602,70.402z"/><path d="M218.561,38.529H22.5c-12.406,0-22.5,10.093-22.5,22.5v119.002c0,12.407,10.094,22.5,22.5,22.5h196.061c12.406,0,22.5-10.093,22.5-22.5V61.029C241.061,48.623,230.967,38.529,218.561,38.529z M226.061,180.031c0,4.135-3.364,7.5-7.5,7.5H22.5c-4.136,0-7.5-3.365-7.5-7.5V61.029c0-4.135,3.364-7.5,7.5-7.5h196.061c4.136,0,7.5,3.365,7.5,7.5V180.031z"/>
</symbol>
<symbol id="icon--phone" viewBox="0 0 211.621 211.621">
  <path d="M180.948,27.722C163.07,9.844,139.299-0.001,114.017,0c-4.143,0-7.5,3.358-7.5,7.5c0,4.142,3.358,7.5,7.5,7.5c21.276-0.001,41.279,8.284,56.324,23.329c15.046,15.045,23.331,35.049,23.33,56.326c0,4.142,3.357,7.5,7.5,7.5c4.142,0,7.5-3.358,7.5-7.499C208.672,69.371,198.827,45.6,180.948,27.722z"/><path d="M150.096,94.656c0,4.142,3.358,7.5,7.501,7.499c4.142,0,7.499-3.358,7.499-7.5c-0.002-28.16-22.916-51.073-51.078-51.077c-0.001,0,0,0-0.001,0c-4.142,0-7.499,3.357-7.5,7.499c0,4.142,3.357,7.5,7.499,7.501C133.909,58.581,150.094,74.765,150.096,94.656z"/><path d="M133.5,132.896c-11.432-0.592-17.256,7.91-20.049,11.994c-2.339,3.419-1.463,8.086,1.956,10.425c3.419,2.339,8.086,1.463,10.425-1.956c3.3-4.825,4.795-5.584,6.823-5.488c6.491,0.763,32.056,19.497,34.616,25.355c0.642,1.725,0.618,3.416-0.071,5.473c-2.684,7.966-7.127,13.564-12.851,16.188c-5.438,2.493-12.105,2.267-19.276-0.651c-26.777-10.914-50.171-26.145-69.531-45.271c-0.008-0.008-0.016-0.015-0.023-0.023c-19.086-19.341-34.289-42.705-45.185-69.441c-2.919-7.177-3.145-13.845-0.652-19.282c2.624-5.724,8.222-10.167,16.181-12.848c2.064-0.692,3.752-0.714,5.461-0.078c5.879,2.569,24.612,28.133,25.368,34.551c0.108,2.104-0.657,3.598-5.478,6.892c-3.42,2.336-4.299,7.003-1.962,10.423c2.336,3.42,7.002,4.298,10.423,1.962c4.086-2.79,12.586-8.598,11.996-20.069C81.021,69.07,57.713,37.339,46.576,33.244c-4.953-1.846-10.163-1.878-15.491-0.09C19.097,37.191,10.439,44.389,6.047,53.969c-4.26,9.294-4.125,20.077,0.395,31.189c11.661,28.612,27.976,53.647,48.491,74.412c0.05,0.051,0.101,0.101,0.153,0.15c20.75,20.477,45.756,36.762,74.33,48.409c5.722,2.327,11.357,3.492,16.746,3.492c5.074,0,9.932-1.032,14.438-3.098c9.581-4.391,16.778-13.048,20.818-25.044c1.784-5.318,1.755-10.526-0.077-15.456C177.232,156.856,145.501,133.548,133.5,132.896z"/>
</symbol>
<symbol id="icon--location" viewBox="0 0 217.109 217.109">
  <path d="M108.552,0C62.42,0,24.89,37.535,24.89,83.672c0,32.967,13.802,66.314,39.914,96.437c19.503,22.499,38.831,35.246,39.645,35.778c1.246,0.815,2.675,1.222,4.104,1.222c1.428,0,2.857-0.407,4.104-1.222c0.813-0.532,20.144-13.279,39.647-35.777c26.114-30.124,39.917-63.471,39.917-96.438C192.22,37.535,154.686,0,108.552,0z M108.552,200.4C93.165,188.934,39.89,144.806,39.89,83.672C39.89,45.806,70.691,15,108.552,15c37.864,0,68.668,30.806,68.668,68.672C177.22,144.806,123.94,188.934,108.552,200.4z"/><path d="M108.557,50.449c-18.31,0-33.206,14.897-33.206,33.209c0,18.307,14.896,33.201,33.206,33.201c18.31,0,33.205-14.894,33.205-33.201C141.762,65.347,126.866,50.449,108.557,50.449z M108.557,101.859c-10.039,0-18.206-8.165-18.206-18.201c0-10.041,8.167-18.209,18.206-18.209c10.038,0,18.205,8.168,18.205,18.209C126.762,93.694,118.595,101.859,108.557,101.859z"/>
</symbol>

Finally, you can rewrap the whole collection with SVG tags. This whole chunk of markup goes inside a visually hidden element, which is then tacked on to your page template so that it loads on every page. In my case, the final product looks like this:

<div class="visually-hidden">
  <svg xmlns="http://www.w3.org/2000/svg">
    <symbol id="icon--mail" viewBox="0 0 241.061 241.061">
      <path d="M198.602,70.402l-78.063,68.789l-78.08-68.79c-3.109-2.739-7.848-2.438-10.586,0.669c-2.737,3.108-2.439,7.847,0.67,10.586l83.039,73.159c1.417,1.248,3.188,1.872,4.958,1.872s3.542-0.624,4.959-1.873l83.022-73.159c3.107-2.738,3.406-7.478,0.668-10.586C206.449,67.964,201.711,67.664,198.602,70.402z"/><path d="M218.561,38.529H22.5c-12.406,0-22.5,10.093-22.5,22.5v119.002c0,12.407,10.094,22.5,22.5,22.5h196.061c12.406,0,22.5-10.093,22.5-22.5V61.029C241.061,48.623,230.967,38.529,218.561,38.529z M226.061,180.031c0,4.135-3.364,7.5-7.5,7.5H22.5c-4.136,0-7.5-3.365-7.5-7.5V61.029c0-4.135,3.364-7.5,7.5-7.5h196.061c4.136,0,7.5,3.365,7.5,7.5V180.031z"/>
    </symbol>
    <symbol id="icon--phone" viewBox="0 0 211.621 211.621">
      <path d="M180.948,27.722C163.07,9.844,139.299-0.001,114.017,0c-4.143,0-7.5,3.358-7.5,7.5c0,4.142,3.358,7.5,7.5,7.5c21.276-0.001,41.279,8.284,56.324,23.329c15.046,15.045,23.331,35.049,23.33,56.326c0,4.142,3.357,7.5,7.5,7.5c4.142,0,7.5-3.358,7.5-7.499C208.672,69.371,198.827,45.6,180.948,27.722z"/><path d="M150.096,94.656c0,4.142,3.358,7.5,7.501,7.499c4.142,0,7.499-3.358,7.499-7.5c-0.002-28.16-22.916-51.073-51.078-51.077c-0.001,0,0,0-0.001,0c-4.142,0-7.499,3.357-7.5,7.499c0,4.142,3.357,7.5,7.499,7.501C133.909,58.581,150.094,74.765,150.096,94.656z"/><path d="M133.5,132.896c-11.432-0.592-17.256,7.91-20.049,11.994c-2.339,3.419-1.463,8.086,1.956,10.425c3.419,2.339,8.086,1.463,10.425-1.956c3.3-4.825,4.795-5.584,6.823-5.488c6.491,0.763,32.056,19.497,34.616,25.355c0.642,1.725,0.618,3.416-0.071,5.473c-2.684,7.966-7.127,13.564-12.851,16.188c-5.438,2.493-12.105,2.267-19.276-0.651c-26.777-10.914-50.171-26.145-69.531-45.271c-0.008-0.008-0.016-0.015-0.023-0.023c-19.086-19.341-34.289-42.705-45.185-69.441c-2.919-7.177-3.145-13.845-0.652-19.282c2.624-5.724,8.222-10.167,16.181-12.848c2.064-0.692,3.752-0.714,5.461-0.078c5.879,2.569,24.612,28.133,25.368,34.551c0.108,2.104-0.657,3.598-5.478,6.892c-3.42,2.336-4.299,7.003-1.962,10.423c2.336,3.42,7.002,4.298,10.423,1.962c4.086-2.79,12.586-8.598,11.996-20.069C81.021,69.07,57.713,37.339,46.576,33.244c-4.953-1.846-10.163-1.878-15.491-0.09C19.097,37.191,10.439,44.389,6.047,53.969c-4.26,9.294-4.125,20.077,0.395,31.189c11.661,28.612,27.976,53.647,48.491,74.412c0.05,0.051,0.101,0.101,0.153,0.15c20.75,20.477,45.756,36.762,74.33,48.409c5.722,2.327,11.357,3.492,16.746,3.492c5.074,0,9.932-1.032,14.438-3.098c9.581-4.391,16.778-13.048,20.818-25.044c1.784-5.318,1.755-10.526-0.077-15.456C177.232,156.856,145.501,133.548,133.5,132.896z"/>
    </symbol>
    <symbol id="icon--location" viewBox="0 0 217.109 217.109">
      <path d="M108.552,0C62.42,0,24.89,37.535,24.89,83.672c0,32.967,13.802,66.314,39.914,96.437c19.503,22.499,38.831,35.246,39.645,35.778c1.246,0.815,2.675,1.222,4.104,1.222c1.428,0,2.857-0.407,4.104-1.222c0.813-0.532,20.144-13.279,39.647-35.777c26.114-30.124,39.917-63.471,39.917-96.438C192.22,37.535,154.686,0,108.552,0z M108.552,200.4C93.165,188.934,39.89,144.806,39.89,83.672C39.89,45.806,70.691,15,108.552,15c37.864,0,68.668,30.806,68.668,68.672C177.22,144.806,123.94,188.934,108.552,200.4z"/><path d="M108.557,50.449c-18.31,0-33.206,14.897-33.206,33.209c0,18.307,14.896,33.201,33.206,33.201c18.31,0,33.205-14.894,33.205-33.201C141.762,65.347,126.866,50.449,108.557,50.449z M108.557,101.859c-10.039,0-18.206-8.165-18.206-18.201c0-10.041,8.167-18.209,18.206-18.209c10.038,0,18.205,8.168,18.205,18.209C126.762,93.694,118.595,101.859,108.557,101.859z"/>
    </symbol>
  </svg>
</div>

Voila! That’s your sprite. Now let's check out how to place and style your individual icons.

Placing and styling your icons

This is where it all comes together. You can place your icons using a simple, no frills use tag inside of an SVG declaration. Remember that this markup is rendered just like an image, except that you can control many of its attributes like color and size with CSS. Your viewBox declaration should retain the original aspect ratio if you don't want the icon to appear skewed, but the exact dimensions aren't important. To place our mail icon following the examples above we’d just use this markup:

<svg class="svg--icon icon--mail" viewBox="0 0 24 24">
  <use xlink:href="#icon--mail" />
</svg>

Now we can target the icons via CSS using icon--mail and other similar selectors. Check out the example of some base styles below. Notice the declaration of fill: currentColor for the svg element. This tells SVG icons to default their fill color to the parent element's computed color, which makes further styling a snap. You can always override this with greater specificity, something like svg.icon--mail { fill: red; }

svg {
  fill: currentColor;
}
 
.svg--icons .icon--mail {
  color: blue;
}

Now you can use your mail, phone and location icons in all sorts of different contexts with whatever styles make the most sense. By default, they’ll inherit fill color from their parent elements. I’ve dumped all of the SVGs, sprite markup, and styles into a simple downloadable project here where it’s all working together as intended. You can see the markup & styles working in the CodePen below.

There are lots of different ways to work with SVGs in your web project, but this implementation is a great combination of simple and flexible. It’s also pretty easy to integrate into existing templates & styles — check out my previous post about SVG Sprites in Drupal 8 menus, for example.

Do you have a favorite approach to injecting SVGs and styling them in your web projects? Feel free to share in the comments below.

Code

Read This Next