Today, nearly every webpage contains some kind of infographic. Infographics are a nice design feature, but also help readers understand and digest content easier. There are several options for dealing with sets of images/icons. Yet from a front end developer's perspective, we prefer SVG graphics, which have become widely supported on all modern browsers and are in vector graphics format, which make them easily scalable on different screen resolutions. Moreover, developers have the ability to change SVG graphic properties through code, because SVG is an image and a XML document. This creates myriad options on how to use them. Following are three different approaches.
Using them as regular images--spriting
The simplest method of using SVGs is the same as loading any other resource--direct link with URL. This occurs when you add the image URL to the <img> tag or url(“”) in your CSS file. This method has the same disadvantage as any other type of resource linking: every item is a separate HTTP request, and it’s usually a render-blocking request. These requests slow down the page load. The solution is simple enough and is a well-known technique--**CSS image spriting**. Spriting means that all the images will be put into one file. For the browser it takes less time to download one large resource versus a large queue of small resources. Additionally, if you use an automatization tool for spriting, it helps easily maintain large sets of icons without worrying if you made an error in your URL address.
Devbridge uses the Gulp automation tool for CSS spriting. It is called gulp-svg-sprite (available on Devbridge's front end starter toolkit on GitHub). This tool is made to work with Sass preprocessor (SCSS syntax). It takes SVG files from a needed folder, puts them into one image file and creates a map of icons, including the mixin for using them on a project’s SCSS code. For example, if you add an icon (in this case, named as icn-cart.svg) and generate the sprite, you will reach your icon with calling this mixin:
@include svg-sprite(icn-cart);
It doesn‘t matter how many icons you have. Only one URL will generate one request on your CSS output. The gulp-svg-sprite tool automatically sets coordinates of the sprite file to show only the needed icon. The CSS output would look like this:
.icn-cart {
width: 477px;
height: 477px;
background-image: url("images/sprite.svg");
background-repeat: no-repeat;
background-position: -2924px -1913px;
}
Seems simple? It is. This tool has a lot of options, such as SVG optimization, spaces between icons (on the sprite file) and custom CSS output templates.
One of the biggest advantages of SVG in general is the ability to change its graphic parameters directly on the image source code. Style properties can be changed using CSS also, but we lose those advantages if we are downloading SVGs as external resources. For example, if you want to have the same icon, but in different colors (such as on active state), you have to create two separate files with different fill, stroke or other values.
Pros
Easy to use like any other web image types
Sprite image file size is fairly small and it's downloaded and rendered once; also cached very easily
A lot of different automatization tools for generating sprites
Cons
You don‘t take advantage of all the benefits that SVG offers. For every icon, which has different styling attributes, you will need a new file
CSS Image sprites have limitations, when it comes to positioning. It‘s because the icon is used as "background-image“ with coordinates set on “background-position." If you want to define the position of the icon regarding the position of the parent element, you’ll need a pseudo element on CSS (::before, ::after), however not all HTML tags support pseudo elements, so you'll end up adding extra markup only for the graphic element
Inline SVGs
As previously mentioned, SVG isn‘t a typical image. Rather, it‘s a document, where all the graphics are written in XML-like syntax. Therefore, the SVG image source code is fully readable, which means that it is possibile to use all the SVG code inside your markup-–HTML supports <svg> tag.
Using inline SVG code like this opens up numerous opportunities to manage the image in various ways. It‘s possible to add a class to the element and manage it using CSS (CSS supports SVG properties, such as fill, stroke, stroke-width, stop-color and others), change properties of separate shapes inside the image, and also control an image dynamically through the app’s code. However, adding inline code to markup works fine if it‘s one or two images. If you have a large icon set, copying and pasting every icon probably wouldn’t be efficient. However, there are options on how to automate this process.
One option is a resource loader that can be used on your Web app’s source code. Loaders can scrape SVG image code on the server-side run or during the build process. The loader will automatically add the image’s code to the markup inline. Several tools can do this, depending on which programming language your project runs. This method doesn’t touch the image code while working on an app’s source code, however, you’ll be able to add external classes and control the image using CSS.
Inline SVG code, in general will not generate any render-blocking HTTP requests. Rather, all the code will be downloaded with the document. However, this increases the document size and basically makes it impossible to cache the images properly (they are not an external resources, so they are cached within the document only).
To avoid cache problems you can use **inline sprites**. Inline sprites means that icons, just like using CSS sprite, are put into a single sprite file, however, every icon is stored as a <symbol> tag on the file and has its own ID. On the markup, you will have to use tag <use> to display icon by selected ID. The <use> tag clones the needed icon from the main sprite file and displays it. However, as the <use> tag clones the icon, it creates a shadow DOM element, hence the possibility of styling the shapes inside the SVG icon are lost (even with CSS). You will be able to change its top-level attributes only.
<svg>
<use xlink:href="#icn-cart" xmlns:xlink="http://www.w3.org/1999/xlink"></use>
</svg>
To load the sprite, CSS image sprites generate a request to the sprite file using url(). There are mainly two options when using HTML code. You can position the sprite file somewhere on the document (e.g., right after the <body> tag) and make it invisible using CSS. However, this option still has the same caching problem, because all the SVG code is injected into the document itself. Another option would be to add a direct link to the sprite file (just like you would do with <img> tag), but including the ID of the needed icon. For example:
<use xlink:href="/path/to/#icn-cart" xmlns:xlink="http://www.w3.org/1999/xlink"></use>
This adds one extra HTTP request, but the sprite file is cached easily. It seems like a perfect solution, but, sadly, it isn’t. Especially, if you need to support Internet Explorer (even on Internet Explorer 11--it’s only fixed on Microsoft Edge). To make the sprite file work cross-browser, you would have to load the file asynchronously using Javascript.
There are different techniques on how to include the sprite file, but you still need to generate it. This can be easily done with the **gulp-svg-sprite** tool. You just need to change its configuration by adding symbol to the mode section (instead of css), which means that every icon will be stored as a <symbol> element (suited for inline sprites).
These two options do not cover all the abilities of inline SVGs. The automatization (loaders or sprites) helps to maintain sets of images. However, SVGs allow you to do dynamic graphics as well (e.g., charts, diagrams, progress bars, etc.). They can use variables directly from your programming code to display real data or even to update immediately. Of course, it would be another, totally different topic about creating diagrams with SVG, because there are numerous tools for this. However, what if you just need to do some custom dynamic graphics? You should ignore the caching problem (since your image is based on the app’s state, probably it doesn’t even need to be cached) and just place inline SVG code to your markup. You don’t need to do that for all the images (especially for the design icons) – just use the methods described above and use inline code for dynamic images only.
We can look at how it would work with React--a Javascript library for building user interfaces that is rapidly gaining global popularity. DOM itself is dynamic on React, it’s updated on every state change, and since SVG code is part of React's virtual DOM, dynamic changes can be applied to SVGs as well. It works fast, and is implemented very easily. However, in general, there are some differences between writing regular HTML and React's JSX code. SVGs are not an exception. For example, if you need to add inline SVG to JSX code, you will have to get all the SVG properties camelCased (for example stroke-width -> strokeWidth). You will still probably need to find a tool, that can automate it for you. However, once you have your SVG code ready for React, you will be able to use the component’s props, state or other variables as its styling attributes. You can even create a separate component for your SVG image and make it reusable, such as in the following example:
import React, {PropTypes} from 'react';
class Icon extends React.Component {
static propTypes = {
fill: PropTypes.string,
height: PropTypes.number,
width: PropTypes.number
};
render() {
const rectangleSize = {
width: 20,
height: 20
};
return (
<svg fill={this.props.fill} height={this.props.height} width={this.props.width}>
<rect
height={rectangleSize.height}
x={this.props.width - rectangleSize.width}
y={this.props.height - rectangleSize.height}
width={rectangleSize.width}
/>
</svg>
);
}
}
export default Icon;
Pros
Opportunity to change graphic properties using CSS
Inline code will not generate any render-blocking HTTP requests
Dynamic, state-based control abilities for building charts, progress bars and other graphic elements
Cons
Every graphic element inside the code will have its own element on the markup. That makes maintaining CSS styles more difficult sometimes
Inline code, especially, if there are many images – increases the document size, and makes them hardly cacheable
External linking to sprites (which resolves the caching problem) doesn’t work on IE, and it’s pretty difficult to get it working
Data URIs on CSS
There‘s an ability to store resources directly to the HTML or CSS file as data URIs. Instead of linking the resources through the URL, which would generate HTTP requests, you can store your data on the document itself. There are no render-blocking requests; only some browser work is required to execute the resource.
However, it‘s impossible to store typical graphic formats (.png, .jpg, or other) on the data URI without converting them to base64. Converting graphics to base64 converts all their binary code to text characters (using just 64 symbols), which makes images usable as a data URI resource. However, the biggest disadvantage of base64 is that it increases the file size significantly (approximately 30% (ceil(x / 3) * 4)).
But what about SVGs? The SVG source is fully readable. However, many people do base64 conversion for SVGs as well. It‘s difficult to say if it‘s lack of knowledge or just a bad habit, but using base64 for SVGs is not only unnecessary, it‘s wrong. It’s already well-supported on all modern browsers and if you encode your SVG code with base64, you will lose all possibility of controlling it dynamically and will have a string of random characters. If you still use base64, consider using CSS sprite, which is significantly faster and does the same thing.
If you copy and paste raw SVG code to the data URI, it probably wouldn‘t work either. Or it may work only on Webkit browsers. If you want full cross-browser support, you will have to escape non-text characters of your SVG file (just like removing HTML tags on text to avoid interpreting them as HTML tags). You can try to do this live here: http://yoksel.github.io/url-encoder/. Of course, that will increase the file size too, but you can still control SVG properties. This processed SVG code now can be easily used as Data URI (e.g., as a background image (data:image/svg+xml and charset must be specified)):
background-image:url(data:image/svg+xml;charset=US-ASCII,%3Csvg%20version%3D%221.1%22%20id%3D%22Capa_1%22%20xmlns
This is fully supported on all browsers that support SVG in general. Changing properties of this code is quite difficult, because it’s still hardly readable. However, you can pick an automation tool, such as Gulp-sassvg, which does this job well. It works similar to spriting tools, which take files from required directory, processes them and creates mixins and functions for your Sass code. Following is an example.
@include sassvg(icn-settings, #777);
On the CSS output, the result of this will look similar as described in the previous paragraph (i.e., code of the image is added as “background-image”). However, as you see in the above example, not only can we include a needed icon, but we also can add additional styling parameters. In this example, the second parameter is fill color (#777). Basically, this tool lets you manipulate your graphics directly on your Sass code with all your design logic in one place. Of course, these styling options are limited (mostly it works on one-color images only). However, if you have a set of icons, and if you just need to have some states that change some of the styling (color, size or similar), it works perfectly fine. Just like using font icons, when you change color or size of the text.
If you look at the CSS output, it seems that this method can easily bloat your CSS with lots of repetitive strings (your file can be 800kb or more, which is huge for a CSS file). However, Gzip compression perfectly deals with the repetition, and drastically reduces file size.
Another concern with this option is the data URIs performance. With CSS sprite, an image is downloaded and rendered once, but for data URIs, every image needs to be executed separately, and the browser does it only on demand (i.e., only if the icon becomes visible). This can cause a delay sometimes, especially if you change a big image on hover.
Pros
Ability to control styling properties using CSS directly
All your style logic on your CSS; no limitations on positioning or markup
No render-blocking HTTP requests. Also, graphics are cached easily with the CSS itself
Cons
Data URI performs slower and adds extra work for browser
CSS file becomes bigger
Conclusion
Using one of our projects, I compared 50 different one-colored SVG icons. I also included 12 active state icons where only the fill color changed. Every method was tested six times on a desktop using Google Chrome. Gzip was enabled. The results were as follows:
CSS Image Sprite
HTTP requests: 91
CSS size: 29.1KB
Sprite file size: 17.8KB
Total data transferred: 409 KB
Page first rendered after: ±669.6ms
Icons were visible on screen after: ±822.3ms
Total load time: ±2.47s
Browser performance:
Render time: ±397ms
Painting time: ±25.6ms
<use> with External Link to Sprite
HTTP requests: 91
CSS size: 27.5KB
Sprite file size: 16.5KB
Total data transferred: 407 KB
Page first rendered after: ±631.5ms
Icons were visible on screen after: ±751ms
Total load time: ±2.38s
Browser performance:
Render time: ±418.5ms
Painting time: ±16.6ms
Data URIs on CSS
HTTP requests: 90 (152 in total, but 62 icons cached inside the CSS, so they’re counted as “request from cache”)
CSS size: 49.3KB
Sprite file size: -
Total data transferred: 412 KB
Page first rendered after: ±767.8ms
Icons were visible on screen after: ±675ms
Total load time: ±2.62s
Browser performance:
Render time: ±591.8ms
Painting time: ±25.8ms
The above results show that the smallest data transfer (and total load time) was using inline SVGs with <use> tag (when adding a link to an external sprite file). However, CSS sprite was very close. The only reason CSS sprite wasn’t the fastest method is because the CSS sprite file had to include separate active state icons while inline sprite had styling changes declared on CSS for active state. Using Data URIs, data transfer is increased, because escaping XML characters increases the file size in general. And it’s clear that the browser is in charge, dealing with rendering of data URIs. However, icons appeared on the screen fastest when using data URIs. This is a perfect example to see how fewer HTTP requests always works better. Also it’s a good UX point to get the first view rendered faster!
The performance difference among these methods is minimal (unless you are using a large number of complex graphics). Therefore, I would recommend using the technique that best suits your needs. If it's just static images, consider CSS image sprites. If, however, you have a set of icons, and you need to make them “live” using active, hover, disabled or other states, data URIs would work (or even inline sprites, if you don’t mind additional markup). If you need the most advanced control of the graphics, use fully inline SVG.
All of these methods are supported in all modern browsers that support SVG in general. The only exception is if you want to utilize the <use>, the inline SVG sprite with external sprite. This method would require additional work with all Internet Explorer browsers.