TIL: Using svg icon sprites with webpack
Today I learned to use convert my font icon to an svg sprite and load it within webpack.
At this point, a lot of things have been written about choosing SVGs over font icons mainly by Chris Coyier of CSS Tricks. I started reading here:
If you're building an icon system for a site, you have some options. If you know the icons need to be raster images…css-tricks.com
followed by this article regarding the use of svg sprites:
I've been a big proponent of icon fonts. Lots of sites really need a system for icons, and icon fonts offer a damn fine…css-tricks.com
Wonderful, no more failed font loads, no more weird symbols. Just inline svg defined at the top of my page, always there, ready to be used. If only, I could use all the icons within my carefully crafted custom generated icon font here.
Turns out that there are solutions to the problem, I read this article by Sara Soueidan:
If you're reading this article, then I can probably assume you've already decided to switch from using fonts for icons…sarasoueidan.com
And I chose font-blast to convert my icon font into svg files inside the directory svg-icons:
npm i font-blast -D
./node_modules/.bin/font-blast my/font/file.svg svg-icons
I proceeded to convert it to an external svg-sprite, I am no longer very partial to grunt, preferring to use either independent tools or something that just works with webpack.
So, I went with svg-sprite-generator, another npm module that converts a directory of svg files into a single sprite. I found it easy to use:
npm i svg-sprite-generator -D
./node_modules/.bin/svg-sprite-generate -d ./svg-icons -o my-font-sprite.svg
Now, I have everything I need, I was pretty sure that I was almost done, but it turns out that what I had assumed to be the trivial exercise turned into something more. How would I load the icons into my html which is generated automatically using the html-webpack-plugin?
As long as I used the webpack-dev-server, the generated assets would not be written to disk, it would instead be in memory. Also, I learned about external svg sprites:
Inline SVG is an awesome way to use SVG because, among other reasons, the individual shapes that make up the graphic…css-tricks.com
Its hard argue against having the browser cache the file separately. So, I needed to load the sprite from an external file, if that failed, I would have to either load it using ajax or inline it within the document.
I tried using the file-loader and was looking at various loaders here:
This is a preprocesser for the webpack module bundler. It support the directive,similar to C #ifdef .webpack.github.io
when I came across this:
SVG has a element which essentially means: go find the chunk of SVG that has this #identifier and kinda clone it and…css-tricks.com
and the svg-sprite-loader which allows loading of svg files using ES2015 imports or CommonJS require statements. It loads the file and provides an id which can be used to reference the icon.
It converts all the svg-files into a sprite and places them into the head of the document at build time. It can also do cache busting by suffixing the id of the icon with the hash of the build.
When you load an svg using either a
I added it to my config and everything was awesome again, I could load each file individually:
import tomato from ‘../images/tomato.svg’
It also allows me to load all my files at once:
I also created the React stateless functional component for Icon as suggested:
and I was good to go, but not before I had to do one more thing. SVGs are usually generated with a lot of namespaces, additional markup, empty attributes and so on. Before rendering, the svg needs to be optimized, use the svgo-loader with the svg-sprite-loader as suggested here:
Hi, I have some svgs (exported from Sketch), which contain element, but loader cuts it from generated sprite. Examples…github.com
For the sake of completeness, let me also add my final webpack config:
Ignore the npm-install-webpack-plugin, it is a particular quirk of mine, I do not like manually installing dev dependencies manually.
Since this article was published, webpack2 was released requiring changes to the configuration above. Assuming that you dont need babel-loader, css-loader and other things for the sake of convenience(or because, the author is too lazy):
The svg-sprite-loader was at 0.x when this article was published, the loader has received a lot of attention since then. It now stands proudly at 3.0.5, its new superpowers include adamantium claws and the ability to take many of the scenarios you might throw at it.
Let’s look at these changes together:
- The plugin’s output has changed, instead of just providing us with an
idit uses an
idalong with a
viewbox. Our Icon component needs to change a little:
2. The plugin has a few more tricks up its sleeve now, you can get it to generate a component for you automatically.
I have pulled the example in the repo into a gist for quick reference:
3. Server Side rendering support was included in the previous version, but we never discussed it, using this example from the repo, I present: