Inline SVG icon sprites are (still) not scary.

One of the best things about HTML is that it just works. As with much of the web, things only get weird when designers and developers start adding things.

This post deals with the specifics of combining SVGs into a sprite. If you're new to SVG, you might find it useful to read this explanation of SVG markup first.

You can see SVG icon sprites in use across the high-performance web. Lonely Planet1, GitHub, & CodePen all use an SVG icon sprite, and there are plenty of advocates for SVG icons across the development community. These examples come from the cutting-edge of front-end development, but SVG icons are not "hard" or "complicated". In fact, using an SVG sprite is one of the easiest ways to maintain an icon system.

Vector-based images are much better than raster formats (JPG & PNG) for creating icon systems for websites. They're easy to style with CSS, and have small file-sizes. The old way of dealing with this was to use icon fonts (where the icons were glyphs in a typeface), but a couple of years ago a better method emerged: SVG icon sprites.

Using an SVG sprite

We could add SVG icons to our sites using standard image-tag syntax – <img src="our_icon.svg"> – but that's not the best option. Every new <img> we add to the page creates a new HTTP request, which is bad for performance. The more requests you make, the longer the site takes to load2. As in the bad-old-days of image-sprites, it makes sense to group all our icons into a single request. Or even better: no extra requests at all. We can do this with SVG icon sprites.

We call them "sprites", but SVG sprites are much more elegant and programmatic than their rasterized forebears. The SVG language allows us to group blocks of icon-code in a single file for individual use later. All our disparate icon files get smushed into one big SVG, which we load like an asset. Then whenever we want to display a particular icon, we reference the relevant part of the master SVG file. No matter how many icons we use, or how often we use them, we're only ever including the complex SVG code once.

The reason we can do this is because SVG is a XML-like language. We can mark out different elements using tags in much the same manner as we'd mark-up our HTML. Instead of using <h1> tags for headings and <a> tags for links, we use tags for lines and shapes. We can also use tags to group our shapes together. The <g> tag ("g" == "group") is the obvious choice, but the <symbol> tag work best for our purposes3. The following code-snippet shows two separate icons within one SVG file:

<svg xmlns="http://www.w3.org/2000/svg">
    <symbol viewBox="0 0 100 100" id="square_icon">
        <path d="M10 10H90V90H10Z"/>
    </symbol>
    <symbol viewBox="0 0 100 100" id="circle_icon">
        <circle cx="50" cy="50" r="40"/>
    </symbol>
</svg>

Notice how we've added a standard HTML id attribute to each <symbol>. This is the "secret sauce" that makes SVG sprites useful. We can now load our entire SVG at the top of our page and extract the symbols whenever we need them.

With the sprite-code in place, using individual icons becomes much simpler. We can include them whenever and wherever we need them by taking advantage of SVG's <use> tag.

<svg>
    <use xlink:href="#square_icon"/>
</svg>

And of course we can still add classes to the <svg> tags, allowing us to style an icon in as many different ways as we like.

<ul class="example_svg_list">
    <li>
        <svg class="example_svg_1"><use xlink:href="#example_svg" /></svg>
    </li>
    <li>
        <svg class="example_svg_2"><use xlink:href="#example_svg" /></svg>
    </li>
    <li>
        <svg class="example_svg_3"><use xlink:href="#example_svg" /></svg>
    </li>
</ul>

Building an SVG sprite with Gulp

Hand-coding SVG icon sprites can be a real chore. It gives your site's users the best experience possible, but it's not fun for developers. Even combining simple SVGs into <symbol> elements inside a single <svg> is not a fun way to spend your time. And real-world SVGs are never as easy to read as the examples show here. What we need is a way to automate the sprite-creation process.

I automate other front-end tasks using a task runner. If you've made it this far into an article about creating SVG icon sprites, then I'm going to assume you use one too. Gulp is my task-runner of choice, but you might be using something like Grunt4. Gulp already handles my javascript and css, so adding my icons into the mix is a natural progression.

Adding an SVG icon sprite to my Gulp setup was quick & painless, and the same task has been serving me well for over two years now. The gulp-svg-sprite module runs error-free without much need for configuration. If you've already got a gulpfile.js in your project, you can install the module like this:

npm install gulp-svg-sprite --save

That module takes individual SVG files and combines them into a single <svg>. Each file becomes a <symbol> with an ID matching the original filename.

So a folder structure like this:

SVG_folder
- icon_one.svg
- second_icon.svg

Becomes a sprite like this:

<svg xmlns="http://www.w3.org/2000/svg">
    <symbol id="icon_one">
        <path d="/* path data */"/>
    </symbol>
    <symbol id="icon_two">
        <path d="/* path data */"/>
    </symbol>
</svg>

Once we've installed the module, we need to build our task. The important things to set are the source folder and destination folder. I.e. where the task will find the raw SVGs, and where it will put the sprite.

// Load the module.
var svgSprite = require("gulp-svg-sprite");

// Set our desired configuration values.
svgConfig = {
    mode: {
        // Make sure we're combining icons
        // using the <symbol> method.
        symbol: true
    },
    // Some more settings to keep
    // the SVG's code clean:
    svg: {
        xmlDeclaration: false,
        doctypeDeclaration: false,
        // By default the module wants to namespace
        // all our IDs and classes. We're grownups
        // so we want to preserve our settings.
        namespaceIDs: false,
        namespaceClassnames: false
    }
};

// Define our task.
gulp.task("svg", function () {
    // Set the source folder.
    gulp.src("uncompressed/icons/**/*.svg")
        // Include our options.
        .pipe(svgSprite(svgConfig))
        // Set the destination folder.
        .pipe(gulp.dest("assets/icons"));
});

With this task in place, maintaining an SVG icon sprite is easy. Whenever we add a new icon to our source folder we just need to run gulp svg. That takes our raw files, cleans them up, and combines them into a single file.

Keeping the intermediate files.

When creating our sprite, the module first cleans-up our SVGs. It removes empty tags, strips out any unnecessary attributes, and minifies the files. This can come in handy if you need easy access to nice, neat SVG code for an individual icon.

If you want to target discrete elements within an icon, you may need to include the raw markup in your page. This can be necessary when you want to build complex animations or transitions. Being able to copy/paste the nicely-configured code is useful in these situations.

You can configure the module to create a folder with the individual SVGs in after they've been optimized and cleaned-up by the module. You can do this by adding the following rule to the svgConfig object:

shape: {
    // Choose a folder to store the
    // intermediate SVG files in.
    dest: "intermediate";
}

Including the sprite in a site

Once Gulp has created the sprite file for us, we need to add that to our page below your opening <body> tag. Copy-pasting the contents of sprite.symbol.svg would do the trick. But we can automate this process by including a reference to our sprite file.

Including an SVG icon sprite in a Jekyll site
In the past I've worked a lot with Jekyll - a static-site generator. When it comes to performance and security it's hard to beat static HTML files. Jekyll compiles a site before it gets put on a server. There are no databases to hack, and no server-side build-steps to slow the site down. Because Jekyll is already combining lots of files together, adding a sprite into the mix is easy.

We can get Gulp to output the sprite into Jekyll's _includes directory by changing the path in gulp.dest(). So .pipe( gulp.dest( 'assets/icons' ) ); becomes .pipe( gulp.dest( '_includes' ) );. Then you can pull in your sprite with a simple "include" statement:

{% include /symbol/svg/sprite.symbol.svg %}

Remember to use Liquid's {% raw %} tag to ensure the SVG code doesn't get escaped.

Including an SVG icon sprite in a WordPress site
I built this site in WordPress, which has its own conventions for including files. When I want to inject code into the markup, `get_template_part()` is the preferred method. The only downside is that `get_template_part()` only works for PHP files.

I get around this obstacle with the following Gulp task:

gulp.task("svg_rename", function () {
    return gulp
        .src("assets/icons/symbol/svg/*.svg")
        .pipe(rename("iconsprite.svg.php"))
        .pipe(gulp.dest("assets/icons"));
});

That task renames the sprite and saves the renamed file in the assets/icons directory. Note that we're adding two file extensions: .svg and .php. Once we've generated this file we include it in our code like this:

<?php get_template_part('assets/icons/iconsprite.svg'); ?>
Hiding the sprite
When we load a page with an SVG icon sprite in it, the browser will try to render the sprite code. This can create a large empty space at the top of our page. The only time we want our SVG code to appear is when we reference it with a tag, so we want to hide the raw sprite. Remember to add a style of `display:none;` to the sprite or it's container.

Building and using SVG icon sprites sounds complicated, and it is. But maintaining a performant and scalable icon system is a non-trivial task. That complexity has to live somewhere, and icon sprites are by far the best of the options5. Couple the sprite with a sensible workflow and the whole process becomes second-nature.

I've been using SVG icon sprites in production for over two years now. I have yet to see a simpler solution. Now that I've got by Gulp setup how I like it, I can be confident that all the tools I need are at my fingertips. Whenever I need to add a new icon to a site, it's a two-step process. I save an SVG to my uncompressed/icons folder and drop a corresponding <use> element in my markup.

Like many good ideas before them, SVG icon sprites are spreading across the web. And like Tyler Sticka said: Don't be Table Guy.


  1. Ian Feather wrote a great article about when Lonely Planet switched from using an icon-font to SVG icon sprites.
  2. The number of requests is a big deal with regular HTTP connection. HTTP-2, however, changes things; but you'll have to find out more about that elsewhere.
  3. We could just use a <g> tag – or even reference individual paths and shapes directly – but <symbol> allows us to add a viewBox attribute. This means we only have to declare the view-box once for each icon, rather than repeating ourselves every time we use the icon in our page.
  4. Note that the raw svg-sprite Node module doesn't read or write to the file system, so you'll need to use something to help you out.
  5. It is worth looking at the counter argument in favour of icon fonts, because SVG sprites may not be appropriate for everybody. Ben Frain probably makes the best case against SVG sprites, but his argument is about changing from fonts to sprites. Kind of a "if it ain't broke" thesis.

Related posts

If you enjoyed this article, RoboTom 2000™️ (an LLM-powered bot) thinks you might be interested in these related posts:

Getting started with inline SVG icons

As a typography nerd, using a custom font to serve icons felt really good. However, it turns out inline SVG icons are better in almost every way.

Similarity score: 95% match. RoboTom says:

Getting to grips with SVG markup

SVGs are complex, for sure, but that very complexity gives them their power. And we don't need to know the intricacies of the co-ordinate system to harness that power.

Similarity score: 90% match. RoboTom says:



Signup to my newsletter

Join the dozens (dozens!) of people who get my writing delivered directly to their inbox. You'll also hear news about my miscellaneous other projects, some of which never get mentioned on this site.

    Newer post:

    What is a decibel, anyway?

    Published on

    Older post:

    Getting to grips with SVG markup

    Published on