Our Current Approach
If you’re building user interfaces then there’s a very good chance you’re going to need some icons at some point. I’ve been building web things for 10+ years now (has introspective moment as she realises this…) and the go-to method I’ve used to manage icons has changed several times, each one aging alongside me (heads to mirror to check for wrinkles…).
In the early days, I used image sprites – carefully laying out each icon and it’s different size options/hover states within a single PNG file. I’d then manually craft the CSS rules to ensure each icon class used the correct dimensions and background position values to match the sprite. Tedious.
From there I discovered icon fonts. Up until a few years ago, we still used Fontello to generate and maintain our icon kits for each project. Slightly less tedious but still a somewhat manual process to update (as well as still being a slightly problematic approach).
Finally, I stumbled upon a blog post by Chris Coyier about SVG sprites a few years back. I trialled a couple of online tools for generating sprites from individual SVG files before settling on Fontastic. We started setting up a new kit on Fontastic for each new project but the process of adding new icons and re-exporting the SVG sprite after each update still felt a little too laborious.
Issues With Using an Online SVG Sprite Tool
Using Fontastic allowed us to ditch icon fonts but the workflow still wasn’t quite right for us. Even though updating the kit and re-exporting the SVG sprite was usually a 10 minute job, it was a 10 minute job that no one wanted to do (especially as it needed doing reasonably frequently).
Another issue we faced was how to manage the logins for Fontastic. Do we create a separate login per client or just have one account with separate icon kits for each project? Either way, it just didn’t seem optimal relying on a third-party tool to manage our clients’ assets – especially in the event that a client chose to move the project in-house or to another provider. Handing over an SVG file that would need to be extracted out and reintegrated next time it needed updating would be a bit of a jerk move.
SVG Sprite Generation Options
So what are the options for generating SVG sprites? Obviously there are services like Fontastic which are basically online tools to manage your individual icon files and export as your preferred format. We used Fontastic but other services such as IcoMoon and Fontello offer similar functionality.
The more obvious (and probably more popular) option is to use a package like grunt-svg-sprite or gulp-svg-sprite as part of your Grunt or Gulp build process (Webpack probably has similar options in this vein also). This would probably be our chosen option if we worked with a consistent stack across all projects and could bake an SVG sprite task into our default setup (more on this below).
Finally, you could look to leverage a JavaScript package like svg-sprite to generate an updated version of your SVG sprite on the fly via the command line (using a script in your package.json file).
The Right Approach for Media Suite…
Before I describe the approach we’ve settled on, it’s probably important to outline the requirements of our solution as they may very well differ from yours (and thus not be the best solution in your case).
For a start, we still need to support Internet Explorer 11 for most of our projects. We work with a lot of government departments and organisations which still require wide-reaching browser support. This doesn’t actually affect which method we choose for generation but it does mean we need to consider how we include the SVG icons in our HTML (more on this below).
The other key factor is that our projects don’t always align with a consistent stack. The nature of the work we do means that we either don’t get to dictate the stack or we have to make a per-project decision regarding which stack is best suited to solving a particular problem (given constraints such as time/money, user base, government requirements, integrations with other software etc).
This means we sometimes work on projects using content management systems like WordPress and SilverStripe, we sometimes build bespoke software using SPAs like React, Ember and Angular or sometimes we’re putting together a custom prototype as a proof of concept.
The point I’m making is that we don’t have a one-size-fits-all approach that we can roll out with each new project. The way we systemise our workflow needs to be a bit more granular so that we can put together a recipe that improves efficiency, quality and maintainability for each individual use-case.
Our Solution (Or the TLDR;)
As mentioned above, the svg-sprite package includes a CLI version which is pretty straightforward to get up and running (see the CLI usage documentation for more details).
1. Install svg-sprite Package
The first step is to install the svg-sprite package in your project (either via NPM or Yarn) like so:
// NPM
npm i -D svg-sprite
//Yarn
yarn add -D svg-sprite
Note: if you don’t have a package.json file yet, run npm init (or yarn init) before installing svg-sprite.
2. Add a Task to Your Package.json Scripts
The goal is to keep all your individual SVG icon files stored in one directory and be able to automatically compile these into a single SVG sprite file on the fly via the command line.
We can do this with the CLI options provided by the svg-sprite package but to make things even easier, we can add an ‘update-sprite’ task to our package.json scripts. This way we don’t need to remember the config options each time we want to run it. Hooray!
svg-sprite is super configurable so you can tweak it to output things in a format that suits you. For our projects, we just want to take all the individual SVG files currently sitting in our icons directory and spit them out as an SVG sprite using the <symbol> element.
For simplicity’s sake, let’s assume the following basic file structure for these examples:
We want to combine the heart, cat and arrow SVG files into a single SVG sprite file called icons.svg and output that in the icons directory (alongside the individual-icons directory).
To do this, we add the following task to our package.json file scripts:
“scripts”: {
… existing scripts here…,
“update-sprite”: “svg-sprite -s --symbol-dest icons --symbol-sprite icons.svg icons/individual-icons/*.svg”
}
You can review the CLI options documentation for more details but here’s a quick breakdown of what each option does:
- -s: run svg-sprite in symbol mode (several other modes are available but not what we’re after)
- –symbol-dest: location to output the generated SVG sprite file
- –symbol-sprite: filename for generated SVG sprite file
- icons/individual-icons/*.svg: this is the location of the individual sprite files
3. Output Icons In index.html
To use our new SVG sprite, we should be able to add the following to our index.html file:
<svg>
<use xlink:href="icons/icons.svg#heart"></use>
</svg>
The above should output the individual icon specified after the hash symbol (see Chris Coyier’s article regarding SVG <use> with External Reference for more information about including inline SVGs this way).
Only hurdle left is… IE11 🙂
4. Polyfill for Internet Explorer Support
To utilise <use> with an external reference in Internet Explorer you’re going to need Jonathan Neal’s SVG for Everybody script.
First, install the package like so:
// NPM
npm i -D svg4everybody
// Yarn
yarn add -D svg4everybody
Then include and initialise it in your HTML like so:
<script src="/node_modules/svg4everybody/dist/svg4everybody.min.js"></script>
<script>svg4everybody()</script>
That’s it – your icons should be working in all major browsers now!
Taking It Further
That’s the most basic implementation using svg-sprite but you can definitely extend the functionality a bit further to suit your needs. As mentioned above, svg-sprite supports other modes of sprite output if inline embedding using an external reference doesn’t tickle your fancy.
We also have the ability to output an example HTML file to preview all the icons included in the generated sprite. This can be really useful for checking for any malformed SVG that isn’t rendering correctly etc. To generate this, you can add the following script to your package.json:
"update-sprite-with-preview": "svg-sprite -s --symbol-dest icons --symbol-sprite icons.svg --symbol-example true --symbol-example-dest index.html icons/individual-icons/*.svg"
If you want to view it in the browser easily, you could add an additional script to serve it up at localhost:8000 (note: if you’re not on OSX or Linux then you may need to install Python before using SimpleHTTPServer)
"serve-example": "npm run update-sprite-with-preview && cd icons && python -m SimpleHTTPServer"
You can find all code examples shown above in my Easy Peasy SVGeezy repo.