Workflow for creating SVG sprites with NPM scripts

In this blog post, I would like to demonstrate a simplified workflow for creating SVG sprites in front-end projects. This task will be done by writing a couple of NPM scripts and Node.js scripts.

NPM scripts reside within package.json file in the script section. You can run them with npm run myscript where myscript is a name of the script. For instance, if you have

you can run npm run buildcss to compile SASS files to CSS. There are some reserved script names which don’t require run. One of such name is start. In the example above, simple execute the command npm start to delete the folder dist and compile SASS to CSS after that.

Let’s create our SVG sprite from various single SVG icons and place it inline in the HTML. Inline SVG sprites are working cross-browser and don’t require any polyfills. An inline SVG sprite looks like as follows:

Read here why symbol is a better choice for icons. As you can also see, an inline sprite should be placed within a hidden container element. Now, when we want to use the icons from the sprite somewhere in the HTML, we can reference them by identifiers as follows:

Let’s automate our tasks. For that, we need to install the following dependencies:

They can be installed as usual with NPM, e.g. npm install ejs --save-dev. A short description of the most important packages is not to be missed:

We want to achieve the following sequence of goals with automated tasks:

  1. Optimize all SVG icons with SVGO by using imagemin-svgo.
  2. Minify all SVG icons with imagemin.
  3. Combine all SVG icons into one sprite which can be used inline.
  4. Embed created sprite into the HTML file automatically.
  5. Create a demo page with all available SVG icons (handy for developers).

In one of my current project, I’ve grouped and placed all SVG icons below the path src/main/resources/svg/icons.

The presented tasks and the project structure lead to the following NPM scripts:

The execution order of scripts is ensured by using the pre and post hooks. Script names started with such prefixes are running automatically by npm before and after their corresponding script. For instance, the command for the script prebuildsprite is running before the command behind the script buildsprite.

First of all, we delete the dist folder. This is a temporary folder the optimized and minified icons will be copied into. The optimization and minification can be achieved by the following Node.js script:

This script gets executed by node imagemin-svgo.js — a quite common execution of any script in the Node.js environment. The imagemin-svgo has a lot of plugins doing various optimization. We remove style and title elements in the SVG definitions, round numeric values to the fixed precision, etc. The next step is the sprite creation. This step is done by the svgstore tool. We say it: “take all optimized icons below the dist/svg folder and build a sprite named casa-svgsprite.svg to be used inline”.

In the last step, we have to put the content of the created sprite into the HTML (s. the markup above). In the HTML (say index.html), we have to use the simple syntax of the templating language EJS. With the scriptlet tag <%- … %> you can output any unescaped value into the template. In our case, the sprite’s content acts as value. The template itself could be written as follows:

For the sake of simplicity, we will write the template as ES6 string directly in the Node.js script embed-sprite.js. This Node.js script gets executed by the NPM script embedsprite.

Last but not least, we would also like to generated a demo page with all available icons (remeber?). EJS allows to use any JavaScript statements inside of the<% … %> scriptlet tag. We could e.g. iterate over an array of filenames (files with SVG icons) by forEach and output icons having filename as identifier. This is shown in the template svgsprite-demo.ejs.

Here, we have three values passed in from outside:

  • theme — content of the CSS theme file which is put inline.
  • svgsprite — content of the SVG sprite which is put inline.
  • files — array of filenames of files containing SVG icons.

Finally, the remaining part of the embed-sprite.js looks like as follows:

That’s all. Now, when we run npm start, we will see something like in the output:

$ npm start
> npm run rimraf && nnpm run buildsprite && npm run embedsprite
> web-theme@1.0.0 rimraf D:\devsbb\vermittler-pos\web-theme
> rimraf dist
> web-theme@1.0.0 prebuildsprite D:\devsbb\vermittler-pos\web-theme
> npm run imageminsvgo
> web-theme@1.0.0 imageminsvgo D:\devsbb\vermittler-pos\web-theme
> node imagemin-svgo.js
SVG-Icons were successfully optimized
> web-theme@1.0.0 buildsprite D:\devsbb\vermittler-pos\web-theme
> svgstore -o src/main/resources/svg/casa-svgsprite.svg dist/svg/**/*.svg --inline
> web-theme@1.0.0 embedsprite D:\devsbb\vermittler-pos\web-theme
> node embed-sprite.js

An optimized sprite has been created and embedded into the index.html. The demo page svgsprite-demo.html with all SVG icons has been created as well (see the screenshot at the top of this post — just a piece of the whole page).