Creating SVG Sprites using Gulp and Sass

Written by Mike Street on 11th November 2014

(Last updated 26th April 2017)

Creating SVG Sprites using Gulp and Sass

Following on from our recent blog post about SVG Sprites which gave an introduction and overview to using SVGs in a sprite, this post will outline the processes and tools we use for creating and using an SVG Sprite at Liquid Light.

Creating and maintaining large SVG sprites can be cumbersome and time consuming, so we decided to automate the process. Rather than managing a single large SVG sprite and tracking the coordinates of each icon individually, we wanted to be able to edit each icon and have the creation and co-ordinate generation automated.

In practice this means that we are able to put all our SVG icons into a folder and the SVG sprite is created and optimised automatically along with a Sass map or names and co-ordinates. By using Sass mixins we are then able to include our sprites by using a very simple bit of code:

button {
    &:before {
        @include sprite(search);
        content: '';

All the code can be found in a repo over on Github

Automating the process

To integrate SVG sprites into our workflow, we decided we wanted a task runner to create the sprite - this meant that we could individually create and update the individual icons without editing and updating the whole image. Gulp is the task runner of choice, running (amongst other things) gulp-svg-sprites. We also wanted the CSS to be created automatically - with the dimensions and background positions calculated upon creating. This gives the advantage of being able to alter an icons dimensions and the CSS updates to reflect this.

By default, the gulp-svg-sprites plugin generates its own CSS, but typo3 has its own classes so we needed a way to create the dimensions and positions as variables, and allow us to use them on existing selectors. For this, we decided to turn to Sass.

Using Sass, the icons are stored in an array - or "map" (find out more about Sass maps). Using some custom mixins, we are able to call on any icon in the sprite and, upon compilation, output the dimensions and background position of each icon.

This blog post is not an introduction to Gulp or Sass (there are plenty of awesome ones around the web for that e.g. ones by Mark Goodyear, Sitepoint and Codefellows ) but rather a post detailing the specific workflow we have for creating and using SVG Sprites. It will run you through the gulp plugins, the gulp tasks we have set up and the specific mixins we use.

The Gulp Plugins - Installation

To run the gulp tasks we first need to install some packages from npm. Run the command below to install the required packages (and gulp itself) and saves them to your package.json.

$ npm install gulp gulp-size gulp-svg-sprite gulp-util --save-dev

Note: If you don't already have a package.json run npm init to create one.

A quick run down of why each of the plugins are there

  • gulp-size - This outputs the size of various files for the user
  • gulp-svg-sprite - this is the heavy lifter, creating the SVG sprite and CSS
  • gulp-util - Used for outputting coloured messages to the screen

Once installed, ensure you inlcude them at the top of the gulpfile.js.

var gulp = require('gulp');
var $ = {
	gutil: require('gulp-util'),
	svgSprite: require('gulp-svg-sprite'),
	size: require('gulp-size'),

We declare them in a $ object to group them.

The Gulp Task - gulpfile.js

At the top of the gulpfile, we delcare a basePaths and paths object. This enables us to group and use paths as variables - making it easier to update and transport to other projects.

We have several gulp tasks to make the sprite and accompanying files (see below). The first task sprite watches a specified folder; any SVG files added or edited trigger the task and the sprite (with accompanying scss file) is created.

Individual SVG files are passed through a SVG optimiser before being combined into a sprite to ensure the sprite file is as small as possible.

We store all our paths and plugins in objects at the beginning of the file, but they can simply be replaced in the appropriate places below (a full file download can be found in the Github repo).

Warning: Mac safari produced different results (when using ems for background position) than any other browser when the sprite was created in a vertical or horizontal way. Diagonal is the only layout where all browsers behaved the same

Warning: Make sure your sprite does not exceed dimensions of 2300px x 2300px - otherwise <= iOS7 won't display the image at all.

gulp.task('sprite', function () {
	return gulp.src(paths.sprite.src)
			shape: {
				spacing: {
					padding: 5
			mode: {
				css: {
					dest: "./",
					layout: "diagonal",
					sprite: paths.sprite.svg,
					bust: false,
					render: {
						scss: {
							dest: "css/src/_sprite.scss",
							template: "build/tpl/sprite-template.scss"
			variables: {
				mapname: "icons"

The Scss template - sprite-template.scss

To ensure the data comes out of the svgSprites task how we want, we pass in a template using placeholders to generate the data. Our sprite map contains data on the sprite as a whole, plus the individual icons contained within the sprite itself.

Our sprite-template.scss looks like this (I've added new lines for readability):

$icons: (
      sprite: (width: {{spriteWidth}}px, height: {{spriteHeight}}px, svgPath: '../img/sprite.svg'),
    {{base}}: (width: {{width.inner}}px, height: {{height.inner}}px, backgroundX: {{position.absolute.x}}px, backgroundY: {{position.absolute.y}}px),

The {{#shapes}} block in the middle loops over each of the files and populates the file name, dimensions and background position.

Using this template with the gulp-svg-sprite plugin, something like the following is produced:

$icons: (
    sprite: (width: 104px, height: 96px, svgPath: '../img/sprite.svg'),
    facebook: (width: 10px, height: 22px, backgroundX: 0px, backgroundY: 0px),
    twitter: (width: 32px, height: 22px, backgroundX: -20px, backgroundY: -32px),
    twitterHover: (width: 32px, height: 22px, backgroundX: -62px, backgroundY: -64px),

As the positions and dimensions are updated dynamically, we can simply add a new icon to our folder, or alter an existing one and the gulp task would re-run and alter or add the changes.

Scss Mixins & Functions

With the generated map, we could start using the values in our Scss like this:

@import "src/sprite";

.class {
    $twitter: map-get($icons, twitter);
    $sprite: map-get($icons, sprite);
    width: map-get($twitter, width);
    height: map-get($twitter, height);
    background-image: url(map-get($sprite, svgPath));
    background-position: map-get($twitter, backgroundX) map-get($twitter, backgroundX);

You get the idea - it's very long winded and would be required for every icon you wanted to use. The above code doesn't even include converting the px to em!

To alleviate the pain, we have created a bank of Scss mixins to enable the use of the sprite as simple as:

@import 'src/sprite';

$sprite: map-get($icons, sprite) !default;
$baseFontSize: 16px !default;

@import 'mixins';

.class {
    @include sprite(phone);

Rather than just have one mixin that does it all, we broke it down into several mixins, placeholders and functions - meaning we can call particular attributes (for example: there is no point redeclaring the background image, or icon dimensions if the icon is changing on hover).


A couple of base variables need to be set - this just makes maintenance easier:

$sprite: map-get($icons, sprite) !default;
$baseFontSize: 16px !default;
  • $sprite - sets up the variable for the main sprite data (file path, dimensions etc.)
  • $baseFontSize - is used for the mq-px2em calculation


Next, the functions retrieve and return the specified attribute for the specified icon from the sass map. We also include the Guardian's sass-mq library, meaning we have a mq-px2em function available.

// Gets an attribute from the sass map
@function sprite-attr($icon, $attr) {
    $newIcon: map-get($icons, $icon);
    @if $newIcon == null {
        @warn "Can't find an icon with the name #{$icon}";
    @return map-get($newIcon, $attr);

@function icon-attr($icon) {
    $attr: (
        width: sprite-attr($icon, width),
        height: sprite-attr($icon, height),
        x: sprite-attr($icon, backgroundX),
        y: sprite-attr($icon, backgroundY)

    @return $attr;


The placeholders set the background as the SVG sprite.

// Sets background image
%sprite {
    display: inline-block;
    background-image: url(map-get($sprite, svgPath));
    background-size: mq-px2em(map-get($sprite, width)) mq-px2em(map-get($sprite, height));


Lastly we have the sprite mixin. There is an ie-sprite() mixin, which has similar functionality, but uses px instead of em and prepends the selector with an .lt-ie9 class.

// For use with the gulp sprite plugin
@mixin sprite($icon, $type: all) {
    @if $type == all {
        // Shares the backgrounds
        @extend %sprite;

    $iconMap: icon-attr($icon);

    // Outputs dimensions in em
    @if $type == all or $type == size {
        width: mq-px2em(map-get($iconMap, width) + 1);
        height: mq-px2em(map-get($iconMap, height) + 1);

    // Outputs background position in em
    @if $type == all or $type == bg {
        background-position: mq-px2em(map-get($iconMap, x)) mq-px2em(map-get($iconMap, y));

This sprite mixin takes in 2 parameters - the first is the name of the icon you want, while the second is optional and allows the user to specify size or bg to get the specific attributes.


With the gulp process set up and the mixins included, getting an icon to display is simple:

.class {
    &::before {
        @include sprite(twitter);
        content: '';
        float: left;
        margin-right: 0.5em;

    &:hover {
        &::before {
            @include sprite(twitterHover, bg);

The CSS output is this

.class::before {
    display: inline-block;
    background-image: url("../img/sprite.svg");
    background-size: 6.5em 6em;

.class::before {
    width: 2.0625em;
    height: 1.4375em;
    background-position: -1.25em -2em;
    content: '';
    float: left;
    margin-right: 0.5em;
.class:hover::before {
    background-position: -3.875em -4em;

Although it seems long winded, this is no more CSS output than if you'd written it by hand. The SVG gets set as the background for any icons using the sprite. From there, the background positions, widths and heights are set on each selector individually - with px fallback for IE8 and below.

All the code above can be found on Github.

We would love to hear if you have any improvements - either comment below, raise an issue of if you are feeling brave create a pull request!

Update (10/03/2015): gulp-svg-sprites is now discontinued as plugin in favour of the gulp-svg-sprite plugin. The blog and Github repo have now been updated to reflect the new plugin configuration.

Update (15/03/2016): Remove gulp-load-plugins and the hard-coded package.json as updates happen. Also update gulpfile examples to match repo.

Update (26/04/2017): With browser support getting better, all the code referencing the png sprite has been removed (as we no longer support IE8)

This article was posted in Responsive, Development by Mike Street

  • Mike Street

    Mike Street

    Mike is our front-end developer who spends his days buried in CSS and Gulp. His evenings and weekends are spent tinkering with electronics and riding bikes.


Thanks for sharing this! I like the approach and have already integrated it into some of my own work. Much appreciated!

Alexander26/12/2014 20:18

Great tutorial. Do you have any idea how to do it with grunt-svg-sprite? I can't seem to find out how to do the template thingy so that grunt will generate the sass map

DL14/01/2015 10:42

Hi DL,

I've not much experience with Grunt, but looking at the recent Github issues, it appears the author has resolved a bug relating to this. He gives an example of how it can be used -

Once loading in your own template, the code looks very similar to my Gulp examples above

Hope that helps!


Mike Street15/01/2015 10:45

This is really nice job, but I still have a question. How can you make multiple sprites ?
I tried by doing different templates but in the mixin, I'm stuck. Have I to duplicate the mixin as I want different sprites or is there a cleaner way ?

David12/02/2015 09:30

Hi David,

To make multiple sprites, you would need to set up a couple of Gulp tasks - watching different folders and outputting to different names.

The mixins could then be adapted to take a variable with the different sprite names.


Mike Street19/02/2015 08:44

Hi Mike,

Thanks for your reply.
I have done my gulp tasks and I have 3 differents templates files for 3 differents sprites. Where I'm stuck now, is in the mixin.
I want to adapt it this way :
@include sprite(twitterHover, bg, $myMainSprite);
@include sprite(anotherIcon, bg, $mySecondSprite);

But it seems tough to add this variable. Would you have some ideas/suggestions ?


David25/02/2015 14:24

I tried to use gulp-svg-sprite instead of gulp-svg-sprites (as I had problem to install it on Win 7). However I got events.js:72 thro er; // UInhandled error event. ArgumentError: SVGSpriter.compile: "{}" is not a valid mode configuraiton. I guess I should change these lines for gulp-svg-sprite. Does anyone here done it and can tell me what I should change?

cssFile: paths.sprite.css,
preview: false,
layout: 'diagonal',
padding: 5,
svg: {
sprite: paths.sprite.svg
templates: {
css: require("fs").readFileSync(paths.templates.src + 'sprite-template.scss', "utf-8")

Ryan01/03/2015 11:17


Ah I see, that's not a problem. You would need to modify the mixins a bit to take in an extra parameter and to remove the extend (so you can modify the background url). I've knocked up a modified version on Sassmeister - it seems to work in principle!


Since this blog post we have updated my gulp file to user gulp-svg-sprite ( instead of the one used above. The config is slightly different:

The gist above features the config we now use and the slightly updated template. Hope that helps!

Mike Street03/03/2015 10:56


Sorry for my late reply. It works like a charm Mike, thank you very much for your help! Now I can have a really nice use of my sprites.

David20/04/2015 14:08

Really nice article. You should update the article to keep up with the code in your github!


Esen22/05/2015 16:46

I feel like i've missed something here, i'm a bit of a gulp newbie, i get everything compiling fine but when i try to use the mixin to pull in the icon from the map it says it doesn't exist. Have i missed a step? Anyone had this issue before?

Sephora28/05/2015 07:13

Hi Sephora,

Have you included the Sass mixin library into your code? Head over to the Github repository and download the mixin file ( and include it into your Sass - it should work. If you are still having problems, raise an issue ( with your code example and the exact error I can help a bit more.

Mike Street08/06/2015 14:11

Hi I wish some node js guru would come uo with something better than the half baked scss to accompany this. I have had to add in a node task to read the image file contentsm then strip out the file extension and write it to a text file then copy paste these into what I call a proper mixin for this. Please see below a much better mixin that can easily be extended as saves writing each new addition into an x and y css co-ord. Feel free to re-use as you wish.

$Icon: /*alphabetical*/
image1, image2, image3, image4, image5, image6, image7, image8, image9, image10;
@mixin sprite-position-loop($offset, $iconWidth, $iconHeight) {
$offset: $offset - 1;
background-position: $iconWidth * $offset 0;
.checked &{//could be &:hover here
background-position: $iconWidth * $offset ($iconHeight);
@for $i from 1 through length($Icon) {
$Icons: nth($Icon, $i);
.#{$Icons} {
@include sprite-position-loop($i, -80px, -60px);

jamiep20/11/2015 13:19

Thanks for this article. Great stuff and nicely explained. However i am unsure of one thing. How do you size the icons? If i use this "as is", my icons will be sized a lot bigger then the size they have in the svg file. How can i set the size of an icon predictably?

Rob06/01/2016 10:04

Hi Rob,

To resize the icon, you can change the font-size on your element.

For example:

If your body base font size is 16px and your icon is 100px square, setting the font size on the element as 50% (or 0.5em) would make the element 50px square. This is becuase the width and height of the icon is set in ems.

Hope that helps!

Mike Street12/01/2016 16:58

Great solution! Thank you!
But if I use icon name that doesn't exist - I have an error (plumber doesn't help, gulp must be relaunched) like this -
Looks like somehow condition "@if $newIcon == null" doesn't work, right?

Alex M01/06/2016 14:11

Great article Mike!

I've been looking for an SVG background spriting solution for a while, but been having trouble with it. Are you aware of a Grunt version to this solution?

I'm currently using the 'svgmin' plugin (to Optimise the SVG's beforehand) and then the 'svgstore' plugin to generate the SVG sprite. This works fine for inline SVG's, but not so well for SVG's used as background images. Unless I'm missing something?

Steffan Carrington10/06/2016 13:47

Hi Mike,

Very well written article, but can I ask how would you tap into the fill capabilities of an SVG and change the colour of an icon?


Nick Toye13/06/2016 12:57

Thanks for the feedback Alex, Steffan and Nick!

Alex: It looks like you are trying to call an Icon which doesn't exist in the sprite. The mixin throws a warning so that you don't end up chasing your own tail :)

Steffan: I don't have much experience (if any!) in Grunt, so I won't be able to help you i'm afraid. Have you looked at the Grunt version of the Gulp plugin I use? - I also found minifying the SVG before combining into a sprite was slightly wasted exercise as I was processing them after as well. Hope that helps!

Nick: Unfortunately, this is the downside of using an SVG sprite as you can't access the separate elements to add fill or stroke to them. If you wanted to colour the whole icon, you can use image filters - i've knocked up a Codepen to demonstrate:

Mike Street14/06/2016 09:08

Hi, Mike, thank you for your tutorial, I have problem with configurating paths for gulp-svg-sprite.
When i compile files from ./project/src/**/*.html to ./project/tmp/ it works fine with creating the same folder structure for compiled files, but i want to compile and creating sprites from ./project/src/**/images/icons/*.svg to ./project/tmp/ and it don't work, its create me another folder in the root. How can i send my output to destination sub-folder?

Gabrielle21/06/2016 07:58

hello I tried to implement your amazing script in my project but I have an issue

Error: argument `$map` of `map-get($map, $key)` must be a map

Idi I mixed something... the only thing I changed is the folder where the scss files are like : scss/module/_sprite.scss

thank's a lot ... john

John26/07/2016 18:04

Hi John,

It sounds like the generated _sprite.scss is not being included into your main SCSS file. Make sure your import path is correct in your main stylesheet.


Mike Street27/07/2016 09:44

Thank's a lot .. I finnaly mad it.
I was trying to do the @import 'modules/sprite';

$ieSprite: '.lt-ie9' !default;
$sprite: map-get($icons, sprite) !default;
$baseFontSize: 16px !default;

@import 'modules/svgspritemix';

in my style.scss the main scss page that load all my dependencie . I finnaly do the thinf in my _base.scss and it's work thank's a lot for sharing sutch a good workflow!

John30/07/2016 06:52

Hey Mike,

That's a very nice article! This one really got me up to speed with creating svg sprites.

Thanks a lot!

Jeffrey06/08/2016 16:45

The mixins could then be adapted to take a variable with the different sprite names. Could you highlight where I should do that in your mixins? I tried to make multiple sprite. Thanks! Great work!

John17/08/2016 21:32

Hi Mike,

First I would like to say thank you for a great tutorial, how to build gulp scss svg sprite.
In my project everything is working, but somehow image position or image size calculated wrong, as you can see from a screen image a little bit cutted when original icon has solid borders

Do you know what can be an reason?

Anton18/08/2016 18:21

2300x2300? I just made 3x icons for menu (last retina) and this is more then 2300 now )
Don't know how I can use it with this limitation (
Also for few sprites I need to clone gulp task for each of them... :(
Hope one day we can use universal solution for multiple sprites :)

Kitin24/08/2016 12:58

@Kitin: if you have created your menu icons as SVGs why are they so large? Scale them down in size and use "transform: scale()" in CSS to resize them appropriately.

Oliver Rowlands24/08/2016 16:47

Oh, sorry, I can use not so large icons of cource, and use font-size for changing size
Also now I prefer to use data uri instead sprites

Kitin24/08/2016 17:24

Hi Anton, glad you enjoyed it! I sometimes experience this myself, and it's where the generated em decimal points round to sub-pixels. Some browsers round these up while others down. First off, ensure your sprite icon artboards are round pixels in illustrator - this issue can be caused if your artboard is 100.5px. If this still does not resolve the issue, you will need to apply a font-size of 101% or 99% on the before element itself. It seems like a hack but it is the only way i've found to resolve this issue!

John - an example of this can be found below. The sprite mixin has been modified to take an additional parameter:

Mike Street31/08/2016 09:40

Hi! I saw that you have elaborated the workflow for generating several sprites. Could pls someone share the gulp tasks config ( the mixin I have used the example from SassMeister), and I have troubles in configuring gulp.
Thx in advance.

Viktor04/12/2016 00:20

Hi Viktor,

Off the top of my head, you would just need two of the gulp-svg-sprite tasks in your gulpfile, watching different folders and outputting different filenames.

I hope that helps!

Mike Street05/12/2016 17:20

Thx Mike for prompt reply! Managed to setup the workflow for two sprites.

Viktor08/12/2016 11:24

Good job with sass !
by the way you svg config was helpful for my builder -
Thanks a lot!

Dima21/02/2017 17:30

Hi, in my gulp.js sprite task i set dimensions for my icons with this:
dimension : {
maxWidth : 32,
maxHeight : 32
How can i change my sass code from this:
[class*="icon__"]:before {
content: '';
display: inline-block;
float: left;
margin-right: 0.5em;
width: 32px;
height: 32px;
background-image: url(map-get($sprite, svgPath));
background-size: mq-px2em(map-get($sprite, width)) mq-px2em(map-get($sprite, height));

To convert 32px to 2.0625em(value from finished compilation) ,
in that way i can target each icon,and don't repeat width,height and display properties

Gabrielle25/04/2017 19:32

Hi Gabirelle,

With regards to the dimensions, doing something like this:

width: mq-px2em(map-get($iconMap, width) + 1);
height: mq-px2em(map-get($iconMap, height) + 1);

Should work correctly?

With regards to your email and getting two sprites - see the reply above to Viktor and John - a second task would be required in your gulpfile with an update to the mixin required.


Mike Street26/04/2017 17:22

Hello thanks for this great article,
one question though why not using
baseSize: 16
instead of the custom mq-px2em function?

François18/10/2017 12:10

Hi François,

Thanks for the comment. At the time of writing, the SVG sprite plugin did not support this option and, in fact, the one we use for this article does not have that parameter, unfortunately (

It seems, however, the original SVG sprites (note the "s") plugin, which was once marked for discontinuation, is now, once again, in active development ( - it is this plugin that has the baseSize option. Thanks for pointing it out though, I will have a look into using it!


Mike Street18/10/2017 12:27

I've tried to get this code to work with my gulp.
I've have an error when my Sass parse my scss files :
Error in plugin 'gulp-sass'
Error: argument `$map` of `map-get($map, $key)` must be a map

assets_dev/css/utilities/_mixins.scss:67, in function `map-get`
assets_dev/css/utilities/_mixins.scss:67, in function `sprite-attr`
assets_dev/css/utilities/_mixins.scss:73, in function `icon-attr`
assets_dev/css/utilities/_mixins.scss:132, in mixin `sprite`
on line 67 of assets_dev/css/utilities/_mixins.scss
>> @return map-get($newIcon, $attr);

status: 1
file: C:/_projects/svg-test/assets_dev/css/utilities/_mixins.scss
line: 67
column: 13
formatted: Error: argument `$map` of `map-get($map, $key)` must be a map

assets_dev/css/utilities/_mixins.scss:67, in function `map-get`
assets_dev/css/utilities/_mixins.scss:67, in function `sprite-attr`
assets_dev/css/utilities/_mixins.scss:73, in function `icon-attr`
assets_dev/css/utilities/_mixins.scss:132, in mixin `sprite`
on line 67 of assets_dev/css/utilities/_mixins.scss
>> @return map-get($newIcon, $attr);

messageFormatted: assets_dev\css\utilities\_mixins.scss
Error: argument `$map` of `map-get($map, $key)` must be a map

assets_dev/css/utilities/_mixins.scss:67, in function `map-get`
assets_dev/css/utilities/_mixins.scss:67, in function `sprite-attr`
assets_dev/css/utilities/_mixins.scss:73, in function `icon-attr`
assets_dev/css/utilities/_mixins.scss:132, in mixin `sprite`
on line 67 of assets_dev/css/utilities/_mixins.scss
>> @return map-get($newIcon, $attr);

messageOriginal: argument `$map` of `map-get($map, $key)` must be a map

assets_dev/css/utilities/_mixins.scss:67, in function `map-get`
assets_dev/css/utilities/_mixins.scss:67, in function `sprite-attr`
assets_dev/css/utilities/_mixins.scss:73, in function `icon-attr`
assets_dev/css/utilities/_mixins.scss:132, in mixin `sprite`
relativePath: assets_dev\css\utilities\_mixins.scss

René Domingue02/03/2018 21:52

Hi René,

It seems the sprite map is not being generated, or not included at all. Ensure the map is being included and called correctly ($sprite: map-get($icons, sprite) !default;) and that there is an $icons map in the "css/src/_sprite.scss" file. It might be the folders need creating before it can correctly generate the _srpite.scss file.

Let me know how you get on,


Mike Street13/03/2018 17:09

Post a comment

You are currently offline. Some pages or content may fail to load.