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:
ejs
— this is a JavaScript templating language often used in Node.js projects (http://ejs.co). We will use it for automatically inserting generated sprite into the HTML.imagemin
— this is an image minifier which will minify our SVGs (https://github.com/imagemin/imagemin).imagemin-svgo
— this is an imagemin plugin for SVGO (https://github.com/imagemin/imagemin-svgo). SVGO is an optimizer for SVG files which remove a lot of redundant and useless information (https://github.com/svg/svgo).svgstore-cli
— this is a command line tool ofsvgstore
which combines multiple SVG files into one using<symbol>
tags (https://github.com/svgstore/svgstore-cli).
We want to achieve the following sequence of goals with automated tasks:
- Optimize all SVG icons with SVGO by using
imagemin-svgo
. - Minify all SVG icons with
imagemin
. - Combine all SVG icons into one sprite which can be used inline.
- Embed created sprite into the HTML file automatically.
- 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.jsSVG-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).