Creating an SVG sprite sheet with Rollup.js

June 2017

Regardless of what you think about SVGs, they provide an excellent format for vector files and due to their impressive compatibility levels, are definitely here to stay for the foreseeable future.

If you’re not sure how SVG sprites work at all, it might be best to read a separate article on the theory of SVG sprites. A good one by Sara Soueidan can be found here:

To those of you already familiar with SVG sprites, I’ve also created a simple project which concentrates on the use of SVGs. Feel free to fork, use, abuse or otherwise learn from the code available (it’s also MIT open source so honestly, use it as you wish!) See the example on Github.

About Rollup.js

Rollup is a recently released module bundler that performs similar duties as Webpack and Browserify, but instead of focusing on creating all-in-one packages that are optimised for application development, its strength lies more in the creation of single libraries which are compatible with their host environments.

If you want to work with ES6, use import directives (amongst many other features) and enjoy the benefits of tree shaking without the complexities (and boilerplate) that comes with full-on application builders like Webpack, then Rollup.js might be the tool for you.

I happened to use it to create the library for Tag, and it worked really well for producing Node & Browser compatible JS with minimal extra effort.

SVG support

Unfortunately, the SVG support with Rollup isn’t fantastic. At the time of writing, there were a few plugins that sort-of deal with this in various ways, but not quite well enough:

My requirements were fairly straight forwards:

  • Bundle up all available SVGs into a single sprite.
  • Make that sprite available to the document.
  • Create an additional exported map of glyphs which could be used to refer to the specific <symbol> with <svg>/<use> tags.

Stage 0 – Get some SVGs

Personally, I am fond of the open source version of the Iconic icon library. Its clear, clean icons are instantly recognisable and lend well to being re-coloured in their entirety.

There are just over 220 icons in the library, each of which should be individually displayable within your project. The process of doing this is actually quite simple. Just output <svg> onto a page either containing the SVG data or otherwise referencing the .svg file with CSS. It’s not optimal, however.

Enter sprites – a technique of combining multiple assets of the same type into a larger asset, which means optimal network requests for your pages and quicker rendering (because the browser only has one resource to show).

Once you’ve decided on the SVGs you’d like to package up, download them and place them somewhere in your project for referencing.

Stage 1 – Create the icon component

I’m using React in my project, but so long as you’re outputting HTML of some sort, the theory is the same: Create a reusable component that produces an icon given the SVG symbol’s id attribute and some dimensions.

The React component to do this might look something like this:

import GLYPHS from '../../img/svg-sprite';

export function Icon(props) {
    var className = props.glyph.replace(/^#/, '');
    return (
        <svg className="icon" width={props.width} height={props.height}>
            <use xlinkHref={props.glyph} class={className}/>

This would result in the following markup:

<svg class="icon" width="16" height="16">
    <use xlink:href='#iconname-sprite' class='iconname-sprite'></use>

The component takes a glyph name (based on the actual filenames of the .svg files) and dimensions, which default to 16x16, and that’s all there is to it. The real work is done before this point, during build.

Stage 2 – Create the sprite data

This part of the process is quite separate from Rollup. In fact, it could be done with any bundling toolkit.

In summary, the following process is used:

  • Before the Rollup stage, determine whether or not to run the SVG build. This step will be more useful during watch processes as you wouldn’t want to build the SVG when a JS file is being altered — it’s a wasted step.
  • Glob the SVGs – It helps to place them all in a consistent path so they can be simply found.
  • Create a variable to store the sprite and a glyph map – This data will eventually be written to a JSON file by the sprite generator.
  • Parse each SVG found – This step is the longest, and the parsing steps are detailed below.
  • Resolve your promise or callback – Then the rest of the Rollup process can continue.

For each SVG found, the following parsing steps will take place:

  • The SVG XML is loaded into a string and parsed with an XML compliant library – Personally, I found node-xml-lite to work well for this. The SVG format is not complex (outside of the actual path vector data) and doesn’t need a juggernaut solution.
  • The SVG object is then optimised – No changes to the path data are made here, just the SVG XML itself. Things like removing the xmlns, width, and height attributes, then giving it an id attribute based on its filename. The most important step here is converting the actual tag name from svg to symbol, so it can be used within an outer svg tag.
  • The resulting object is then converted back to XML – This was done by a simple string concatenating function, as the XML format is incredibly simple and didn’t require any extra libraries.
  • A JSON file is then created – This file is used by the SVG handler script within the project.

Once the XML string for a single sprite-compatible symbol has been created, it is then returned back to the outer function which concatenates it into the collective sprite.

In addition to this, the same filename-to-id logic is used to create an entry in an ID map array which is used as a helper dataset for referencing the correct symbol by its ID. Think of it as an appendix of the SVGs available.

Stage 3 – Implement into your Rollup build

You’ll find that within a project of any great complexity you may outgrow the basic configuration based rollup and need to start using the JS api. It’s quite powerful and allows complete control of what happens between each stage of the rollup process.

For the JSON file generated in the previous step, all I’ve done is the following:

const json = require('rollup-plugin-json');

And then in the plugins array for the configuration of rollup.rollup I’ve added:

plugins: [
        exclude: 'node_modules'
    }), // ...

That’s all you really need to do within rollup for this particular problem. The previous step generates everything for you and places it into a JSON file, which is then imported by the project and handled like other non-JS imports through the rollup plugins system.

You can then import the JSON file as it were just another asset into the project source with the following:

import sprite_data from '../../dist/svg-sprite.json';

(The reason this file is in dist/ is because it’s a file generated during the build process, and doesn’t actually need to be committed to version control.)

Stage 4 – Place the sprite into the document

After the JSON file is generated by the step 2 and imported with the help of step 3, it then needs to be loaded into your document in some way.

This is done by using the data returned by the JSON import to create an svg element on the document.

Broadly, your project should use the JSON data returned to do two things:

  1. Create an outer svg tag placed in the document and fill it with the SVG data contained within the imported JSON file.
  2. Create a variable containing glyphs which reference the symbol tags within the svg on the document by their attribute IDs.

The resulting svg on the page would look something like this:

<svg style="position: absolute; width: 0px; height: 0px;">
    <symbol viewBox="0 0 8 8" id="account-login-sprite"><path d="..."></path></symbol>
    <!-- the rest of the <symbol> tags... -->

Once that’s done, you can then create SVG references to the sprite frames and produce unique SVGs throughout the document using the same file.

Leave a Reply

Your email address will not be published. Required fields are marked *