SVGs are a great asset in our responsive web design toolkit. But just like any other image format, there are certain steps you should take to make sure you’re delivering optimised resources that don’t have a negative impact on your page’s performance.
Here are some things that you can do to make sure you’re delivering better SVGs for the web.
1. Enable Gzip on your SVGs (if it’s not already enabled)
SVGs are text-based, and thus gzip really well. SVGs contain many repeated fragments of text, so they are well suited for lossless data compression algorithms.
When an SVG is gzipped, it is referred to as an “SVGZ” image and uses the corresponding .svgz
filename extension. An SVGZ file is typically 20 to 50 percent of the original size.
As a matter of fact, Appendix G of the SVG specification — that contains a list of features in SVG that promote small file sizes — contains a list of example compression rates resulting from gzipping a bunch of SVGs. The data shows that SVGs gzip really well, reaching a compression ratio as high as 84% in some of the examples.
The following table shows a few examples from the tests available in the appendix:
Uncompressed SVG | With gzip compression | Compression ratio |
---|---|---|
12,912 | 2,463 | 81% |
13,024 | 2,041 | 84% |
12,164 | 2,553 | 79% |
You can enable SVG gzipping in your .htaccess
file — thus making sure your server serves SVG/SVGZ the right way — by first adding the SVG and SVGZ media types to the list of media types:
# Add this to the list of Media files AddType image/svg+xml svg svgz
Next, add the gzip encoding:
<IfModule mod_mime.c> AddEncoding gzip svgz </IfModule>
This will not gzip your SVGs — it will only make sure the server serves pre-gzipped SVGs correctly.
Then, in order to enable dynamic gzipping, you need to specify AddOutputFilterByType DEFLATE
and then set the type to image/svg+xml
. For example, in the HTML5 Boilerplate .htaccess
file, it looks like so:
<IfModule mod_filter.c> AddOutputFilterByType DEFLATE "application/atom+xml" \ "application/javascript" \ "application/json" \ ... "image/svg+xml" \ ...etc. </IfModule>
HTML5 Boilerplate’s .htaccess
file contains a lot of useful reusable code snippets. Check out the above snippet and more in the boilerplate’s repository on Github.
2. Avoid multiple requests when providing fallback with <object>
The <object>
tag is one of the most common ways to embed an SVG in a web page. It offers many advantages that other embedding techniques don’t — like scripting with JavaScript, and CSS animations and interactions, without compromising cachability of the referenced SVG.
Another reason why <object>
is a great embedding technique is that it comes with a standard and default fallback mechanism — you can provide fallback for browsers that don’t support SVG (or even those that do support it but for any reason can’t display the requested SVG) between the opening and closing tag:
<object data="mySVG.svg" type="image/svg+xml"> <!-fallback -> </object>
In most cases, you will want to provide a bitmap version of your image as fallback. For example:
<object data="mySVG.svg" type="image/svg+xml"> <img src="fallback.png" alt="" /> </object>
The above fallback approach isn’t wrong, but it is an approach you shouldn’t take.
The reason the above fallback isn’t optimal is that SVG-capable browsers will request both the SVG and the PNG images, thus resulting in an extra HTTP request that we need to avoid.
In order to avoid the double requests, there is a workaround first spotted on the ClearLeft website and mentioned by David Bushell: instead of providing the fallback image as a foreground img
, set it as a background image to a <div>
injected between the opening and closing object
tags:
<object data="mySVG.svg" type="image/svg+xml"> <div class="fallback"></div> </object>
.fallback { background-image: url(path/to/fallback.png); /* etc. */ }
When the image is provided as a background to the div
, SVG-capable browsers will not request it — only browsers that don’t support SVG will render and style the fallback div
and hence request the background image.
3. Avoid Embedding SVGs as Data URIs when possible
Minimising HTTP requests is considered one of the best practices for better performance. It is also the number one reason why many of us decide to inline images using data URIs — be that in our HTML or CSS.
Images embedded inline using data URIs are also kinda convenient — they are generally easy to create, embed and maintain.
But..
Avoid embedding SVGs as data URIs when possible.
Data URI’d images do have some advantages, but research shows that they are sometimes significantly slower than CSS image sprites, and thus may have a bad impact on performance — especially mobile performance.
The possible performance impact of inline images is the main reason why inlining everything is not the answer — as Guy Podjarny confirms in an article he shared right here on the Perf Calendar blog.
Based on Podjarny’s and other researches, it is recommended that you limit the use of inlining to small image files. According to Podjarny, “the HTTP Overhead of a request & response is often ~1KB, so files smaller than that should definitely be inlined. Our testing shows you should almost never inline files bigger than 4KB.”
Complimentary to Podjarny’s research, another reseach conducted by Peter McLachlan shows that “15-20kB for max data URI size, and no more than 3 – 5 instances seems like a good rule of thumb for mobile.”
In addition to that, CSS-inline images affect page rendering since the content of the page will not render before the CSS is parsed. Hence, the bigger the CSS file, the longer it is going to take to render the page. So, inlining inside CSS is a render-blocking procedure that can easily have a visible impact on performance that your users may definitely see and feel. That is, unless the images are inlined in a separate non-critical style sheet that is loaded asynchronously.
Also remember that CSS is usually shared across multiple pages. So if your CSS-inlined images are not used across all of these pages, you might be delaying these pages’ rendering and loading unnecessary resources across all of them.
Podjarny also recommends that page images (i.e. images referenced from the page, not CSS) rarely be inlined. “Page images tend to be big in size, they don’t block other resources in the normal use, and they tend to change more frequently than CSS and Scripts. To optimise image file loading, load images on-demand instead.”
Inlining has its advantages and disadvantages — you should be careful not to abuse it. If your images are not critical for above-the-fold rendering, they don’t need to be inlined. If — for any reason — you do inline them, try to load them asynchronously. Same thing applies to inlining in CSS — load the style sheet asynchronously if possible. For example, the workflow used in the Grunticon SVG icon sprite generation process loads the style sheet containing the inlined SVG icons asynchronously. See the last section for more about Grunticon.
HTTP/2 brings some important performance optimisations to HTTP requests. The main advantage of inlining — which is saving that extra HTTP request — will no longer be an advantage because HTTP/2 enables the server to send multiple responses (in parallel) for a single client request — also known as, server push. This means that this will remove the additional HTTP request concern from the equation. HTTP 2.0 server push makes inlining obsolete. And even without server push, HTTP/2 massively brings down the cost of additional requests, so this excuse no longer becomes valid to data URI SVGs.
With all that said, fact remains that HTML-inlined SVGs (be those data URI’d or plain text) cannot be cached, which is also another disadvantage — an HTTP request is saved, but you wouldn’t be leveraging the browser’s caching ability anymore — that is, unless the HTML is cached, which rarely happens. That, and the inlined SVGs will result in extra overhead sent to browsers that may not be able to render it anyway. Feature detection and fallback to non-SVG versions of the graphic might be a good option in a case like this.
4. Don’t base64 inline SVGs
If you do decide to inline SVGs — be that in CSS or HTML — don’t base64 encode them.
Many developers like to base64 encode images — especially SVG. However, one research shows that base64 encoded images can cause serious performance bottlenecks on mobile, but the results of that research were later debunked by another research.
Nevertheless, base64-encoding an image increases the CSS file size dramatically.
SVGs can be embedded inline without base64-encoding them. You can minify the SVG code — removing white space from it — and then embed it as plain text without resorting to base64 encoding. For example, instead of this:
.el { background-image: url('data:image/svg+xml;base64;iVBORw0...'); /* etc. */ }
you would do this:
.el { background-image: url('data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns...;'); /* etc. */ }
The main advantage to using plain text minified (white space removed) SVGs as opposed to base64-encoded SVGs is that the former gzips better. The reason plain text SVG GZips better is that it usually contains a lot of repetitive elements that gzip can easily crunch, significantly reducing file size.
In addition to that, base64 encoding needlessly inflates the content by 30%, and although gzip recovers some of this, you still end up with ~10% bloat for no reason. Therefore, it makes sense at this point why it’s best not to base64 encode your SVGs, should you decide to inline them.
5. use
a cacheable SVG sprite
There are several ways for creating SVG sprites — these sprites are mostly used in SVG icon systems.
One of the SVG spiriting techniques works by referencing individual icons inside an <svg>
container (the actual sprite) using the SVG <use>
tag.
The SVG sprite can be embedded in the document as a code island using the <svg>
tag. The icons would be defined inside the sprite using <symbol>
elements for each icon. Each icon <symbol>
would have an ID. Then, the icons are referenced anywhere in the document using <use>
:
<svg style="display:none;"> <symbol id="twitter-icon" viewBox="0 0 32 32"> <path d="" fill="#000000"></path> </symbol> <remaining icons here> <symbol id="instagram-icon" viewBox="0 0 32 32"> <icon contents> </symbol> <etc> </svg> <!-- somewhere else in the document --> <svg class="twitter-icon"> <use xlink:href="#twitter-icon"></use> <svg>
This works just fine in all browsers supporting SVG. But, as we mentioned earlier, the SVG won’t be cached, and by inlining it like so we will be sending unnecessary overhead to non-supporting browsers. In order to avoid this overhead and leverage browser caching, you can reference the icons in an external sprite. This would initially cause an extra HTTP request, but the sprite will be cached, thus speeding up any subsequent loads. To reference an external style sheet, the previous <use>
would look like so:
<svg class="twitter-icon"> <use xlink:href="path/to/icons.svg#twitter-icon"></use> <svg>
Much better.
Referencing an external sprite in <use>
is definitely recommended. However, it does not work in any version of IE. That being said, there is a polyfill available to allow you to do it. To learn more about the polyfill and about this spriting technique — and others — you can refer to the article I wrote on the 24Ways blog.
6. Optimise Your SVG
Most of the optimisations mentioned so far are made in the last phase — when you actually embed your SVG in your page. However, there are some things you can do in your vector editor that can help you export cleaner code, as well as post-export optimisations, which are one of the most important steps you can do to make your SVGs ready to work with and embed.
There are many SVG editors available — some are free and some are not. But they all have one thing in common: none of them outputs perfectly clean and optimised code. So it is your task to make sure you end with usable, readable and optimised code before you put those SVGs to work. Here are some things you can do to make that happen:
6.1 Optimise in the editor
There are many ways for doing the same thing in graphics editors. But some techniques produce better code than others.
- Simplify paths, and combine them when possible. The less the path points, the less the path data in the resulting SVG code, and hence the smaller the file size. Combining paths can also decrease file size significantly depending on the illustration and how the elements inside it are drawn.
- Avoid drop shadows, inner or outer glows and Gaussian blur, particularly if you’re working with Illustrator. Sketch does a better job at exporting these effects as actual SVG filters and effects, but Illustrator currently converts these effects to raster images which are then either embedded into the SVG — dramatically increasing its file size — or provided as external images that are referenced inside an SVG
<image>
, thus adding an unnecessary extra request. - Organise and name your layers. The layer and group names you use in your editor will be translated into element IDs in the SVG code, so instead of ending up with editor-generated names that make absolutely no sense at all, naming your elements and layers appropriately will save you some work in later stages of your work.
There are certainly more things you can do in the process of your SVG creation to get better exported code, but these are some of the main points particularly related to file size and performance, so we will skip going over other steps for now.
Take advantage of your editor’s export optimisation options if available
In Illustrator, and after you choose to save your file as SVG, you get an options panel that contains a set of options that can also help further optimise your SVG before you export it.
The options in the panel include turning SVG text into outline. This can be useful because it allows you to avoid embedding the font into the SVG. That being said, it may sometimes increase your file size, depending on the font you’re using. For example, decorative fonts may result in larger code when converted to outlines.
You can also choose to apply the styles to the SVG using either SVG presentation attributes or CSS — the latter can sometimes be more useful, particularly if you have a lot of repetitive styles which can be applied to multiple elements using class names in CSS, instead of applying them to each and every element with presentation attributes. Choosing the CSS option may help decrease the resulting file size — not to mention that it makes styling and animating the SVG with CSS simpler.
Another important option is one that allows you to specify the number of decimal points to use in the code. The less this number, the smaller the file size. By default, Illustrator exports the SVG with three or four decimal places. One decimal is usually enough. If you have an SVG that uses four decimal places and you optimise it using a standalone optimisation tool (we’ll talk about these next) and decrease the number of decimal points to one, it can slash your file size down to the half.
It is worth mentioning at this point that it is best to limit the decimal numbers before you export your SVG, because — sometimes — doing this after exporting the SVG could break your SVG. It happened to one of my SVGs once before. The sooner you can make these optimisations, the better it is along the way.
For [a lot] more information about Illustrator’s export options panel, refer to this article by Adobe’s Michaël Chaize.
6.2 Optimise your SVG after exporting it from the editor
Exported SVG code is rarely going to be fully optimal — if ever. Thus, it is recommended that you integrate some kind of optimisation process into your workflow to optimise your SVG before using it in your pages.
Optimising an SVG ensures a smaller file size and more readable — and hence more editable — code.
Code exported from editors usually contains a lot of redundant elements and groups, editor metadata and non-optimal values that lead to bloated code and an increased file size that can be slashed down to the half (or more) using proper optimisations. This unnecessary code bloat be removed or changed without affecting the rendering result of the SVG. Thus, is it generally a good idea to use a standalone optimisation tool to optimise the after exporting them.
Optimising SVGs by hand can be a very daunting task. (Imagine having a big set of images, all of which need manual optimisation, and some of which might be really complex.) So it is definitely best to automate the optimisation process so that it happens “automagically”.
Luckily, some smart people have come to create and share tools that allow us to automate SVG optimisation in different ways. Many tools exist, each with its own requirements and limitations. No matter what your workflow is, one of them is sure to fit in.
If you’re looking for a quick optimisation tool that doesn’t require any setup and gives you visual feedback, Peter Collingridge’s online SVG Editor is an excellent tool. The editor gives you real-time feedback showing you the result of applying your optimisations to the SVG as you apply them. The optimisations are presented as a set of options that you can check and uncheck as needed. This is useful because some optimisations may end up breaking your SVG, so being able to “live optimise” the SVG before exporting the optimised code is great.
If you don’t want to use an online tool and prefer one that can be integrated into your projects, SVGO is going to be your best option. It is a Nodejs-based tool for optimizing SVGs, that is available as a Grunt plugin, Gulp plugin, OS X folder action, and even comes with a simple drag-and-drop GUI.
SVGO is great and — if you use Grunt or Gulp — fairly simple to set up and use. The optimisations are available as a set of plugins that you can enable and disable as needed. I recommend you customize it because some of the default optimisations applied are actually bad for SVG use. For example, the plugin removes the SVG viewBox
attribute by default, which you should not do! The viewBox
is essential and you must make sure it stays where it is, especially if you want to make your SVG responsive.
The only drawback to using SVGO is that the optimisations may end up breaking your SVG; in which case you would need to change the plugin’s configuration and re-optimise again. There is currently no way to tell whether the optimisations will break the SVG or not.
Note that running your SVG through SVGO several times can yield more optimisations every time. Some developers have reported further optimizations even after running SVGO 15 times on their SVGs!
Recently, a tool called SVG NOW was created, which practically integrates SVGO into Adobe Illustrator so you can apply SVGO’s optimisations right in the editor. The plugin provides SVGO optimisations as a set of options. You can check the ones that you want to apply and then export the optimised SVG. Once again, there is no way of telling if the optimisations are suitable for the SVG or not, so you may have to make some changes and re-export again if the exported SVG turns out broken.
These are the three main SVG optimisation tools available today. SVGO is the most powerful tool, but its only current limitation is not being able to see a preview of the optimised SVG before generating it, but that’s certainly not stopping designers and developers from using it today.
7. Test, test, test.
Sometimes SVG may not be the best format for your image — your image might be better off as a PNG or JPEG. Some images make better SVG candidates, and are more likely to be smaller as SVGs than their PNG and JPEG counterparts. On the other hand, some images are best served as bitmaps instead of SVGs, as the SVG version may be too large to serve without affecting the page’s performance.
Small icons and simple illustrations are best candidates for SVG, while more complex images and continuous tone images are better served as PNGs or JPEGs. The best way to determine which format to choose is to compare the sizes, and test the performance of each.
No matter which format you end up choosing, make sure you’re delivering properly optimised assets to your users, and always test the SVGs against other formats to make sure they don’t turn into the cause of your page’s performance bottleneck.
Many thanks to Jake Archibald for tech-editing and reviewing this article.