Despite the introduction of the Scalable Vector Graphic (SVG) format almost 20 years ago, the popularity and understanding of the format amongst the web community has grown phenomenally in the last few years. The format was brought to the table in 1999 by the World Wide Web Consortium (W3C) and is an XML based vector image format designed specifically for the web.
One of the many beauties of the format is that it can be scaled to literally any size, whilst always retaining quality. On the other hand, a bitmap image, such as
.gif, will pixelate when scaled beyond its natural size. Scalability means that SVG are a natural fit for the modern responsive web. An SVG could easily be scaled anywhere from a small mobile display, to a standard desktop display, all the way to a 5K display, and everywhere in between and beyond.
The format brings plenty of other benefits. SVG images are typically smaller than their bitmap counterparts and the format supports other interesting use cases, including animation. If you’re not yet convinced about the SVG format, I strongly recommend watching this Chris Coyer video or reading Chris’ book: Practical SVG.
The aim of this article, however, is not to convince you to use the SVG format in your web projects, but rather a useful SVG optimisation anywhere for those who have already adopted the format.
SVG images behave synonymously with bitmap images when previewed in a browser or image viewer -- it simply looks like a standard graphic. However, since SVG is XML-based, you can find readable, familiar HTML-esque markup under the hood. This means that you have readable line-by-line control, regardless of what tool churned out the file.
Although an SVG could be coded from scratch, most SVGs are produced in some form of graphics editor, such as Adobe Illustrator, Inkscape or Sketch. Generally, these editors export SVGs with additional, unnecessary and sometimes even outdated elements and attributes within the markup.
Here is some XML from a typical SVG graphic created in Adobe Illustrator CC:
<?xml version="1.0" encoding="iso-8859-1"?> <!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> <svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 60 60" style="enable-background:new 0 0 60 60;" xml:space="preserve"> <g> <path d="M45.563,29.174l-22-15c-0.307-0.208-0.703-0.231-1.031 0.058C22.205,14.289,22,14.629,22,15v30c0,0.371,0.205,0.711,0.533,0.884C22.679, 45.962,22.84,46,23,46c0.197,0,0.394-0.059,0.563-0.174l22-15 C45.836,30.64,46,30.331,46,30S45.836,29.36,45.563,29.174z M24,43.107V16.893L43.225,30L24,43.107z"/> <path d="M30,0C13.458,0,0,13.458,0,30s13.458,30,30,30s30-13.458,30-30S46.542,0,30,0z M30,58C14.561,58,2,45.439,2,30S14.561,2,30,2s28,12.561,28,28S45.439,58,30,58z"/> </g> </svg>
If you’re familiar with HTML, this too will look quite familiar -- just elements and attributes. And, just like HTML, we can optimise it.
Let’s take a look at another example. Here is the SVG markup that was generated by Sketch for a "hospital map marker" icon.
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg width="39px" height="39px" viewBox="0 0 39 39" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <!-- Generator: Sketch 39.1 (31720) - http://www.bohemiancoding.com/sketch --> <title>Hospital</title> <desc>Created with Sketch.</desc> <defs> <rect id="path-1" x="0" y="0" width="33" height="33" rx="9"></rect> </defs> <g id="Maps" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> <g id="Map" transform="translate(-483.000000, -779.000000)"> <g id="Hospital" transform="translate(486.000000, 782.000000)"> <rect id="Rectangle-57" fill-opacity="0.599999964" fill="#FFFFFF" opacity="0.5" x="0" y="0" width="33" height="33" rx="9"></rect> <g id="Rectangle-57"> <use stroke="#2D2D2D" stroke-width="6" fill-opacity="0.599999964" fill="#FFFFFF" fill-rule="evenodd" xlink:href="#path-1"></use> <use stroke="#FF0000" stroke-width="3" xlink:href="#path-1"></use> </g> <polygon id="Shape" fill="#FF0000" points="19.7149758 19.5157005..."></polygon> </g> </g> </g> </svg>
The markup totals 21 lines of code. Now, relative to your standard HTML file, this may not seem like a lot, but it’s quite a lot of lines for a simple SVG. I typically work with SVGs of around 4-10 lines maximum.
So, without further ado let’s dig in and see what we can remove.
The first thing that we can remove is the XML prolog.
<?xml version=”1.0″ encoding=”UTF-8″ standalone=”no”?>
Most XML documents begin with a prolog; the prolog being one or more lines of code providing information about the current and related documents. If the SVG is going to be embedded within an HTML document or another SVG, which it most likely will be, the prolog is redundant and can be swiftly removed. Although leaving the prolog will have no effect to the end user, removing it helps to keep your code clean, readable and (very minutely) reduces file size.
<svg> tag comes packaged with the version attribute, specifying that it is using SVG version 1.1.
<svg version=”1.1″ … >
This attribute has no influence or bearing on the rendering of the SVG and can be removed. So, let’s strip that out too.
One thing that particularly stands out to me is the large XML comment that was generated by Sketch.
<!– Generator: Sketch 39.1 (31720) – http://www.bohemiancoding.com/sketch →
Although in certain contexts, XML comments can be helpful, or even important, this particular comment is redundant. Other graphics editors may include XML comments when generating SVGs and these comments can also be safely removed.
Next, we’re going to strip out the
Removal of these elements is dependant on the context in which the SVG is going to be used. The
desc tags both aid accessibility. The
title element may be displayed on hover in certain browsers and the
desc may be displayed instead of the graphic in situations where the SVG paths cannot be rendered (e.g. older browsers, poor internet connection, etc). In other words, the
desc can serve as the
alt attribute for SVGs.
<title>Hospital</title> <desc>Created with Sketch.</desc>
If the SVG provides important context to the webpage (say, for example, a chart or graph), the
desc tags should not be removed. On the other hand, if the SVG is purely aesthetic and offers no information to the end user (say, a small icon), these tags can likely be removed.
Note: If you do decide to keep the tags, it’s important to provide better information than the automatically generated content that the graphics editor provides. Try to provide a rich title and desc that will convey the meaning of the SVG to the user.
One of the major clutters of the SVG markup is the chain of
<g> tags that serve as a wrapper for the SVG paths.
These elements routinely contradict their parent. For example, the second
<g> has a transform attribute with the value
translate(-483.000000, -779.000000) while its child has
translate(486.000000, 782.000000). The attributes, and subsequently the elements themselves, almost cancel each other out. The final transform is just
<g id="Maps" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> <g id="Map" transform="translate(-483.000000, -779.000000)"> <g id="Hospital" transform="translate(486.000000, 782.000000)"> <rect ...></rect> <g id="Rectangle-57"> <use ...></use> <use ...></use> </g> <polygon ...></polygon> </g> </g> </g>
All of these
<g> elements can be removed as they are quite redundant. The graphics editor that generated the SVG knows very little about code optimisation, leaving a trail of endless
<g> tags in its wake!
<svg> viewBox is a difficult subject, and I would strongly recommend Sara Soueidan’s article if you are new to the attribute.
<svg viewBox=”0 0 39 39″ … >
After the removal of the
<g> tags, the positioning of the SVG elements was slightly off by 3px (due to the transform attributes as previously mentioned). I adjusted the starting point of the viewBox to compensate.
<svg viewBox=”-3 -3 39 39″ … >
This isn’t ideal, but it is better than the endless chain of
<g> tags with random, non-sensical transform attributes. You may not have to make any edits to the
viewBox attribute, but if you do then it is likely that you will have to use different values.
Most of the elements within the SVG markup were provided with an
id attribute when the SVG was generated. The
<rect> element, for instance:
<rect id=”Rectangle-57″ … />
id attributes can be stripped out. The only instance where the
id attributes are important is within the
In our case, ours
<defs> block looks like this:
<defs> <rect id=”path-1″ x=”0″ y=”0″ width=”33″ height=”33″ rx=”9″></rect> </defs>
The elements within the
<defs> are not initialised, they are simply declared for future use. You can see this element is used later in the markup:
<use xlink:href=”#path-1″ … ></use>
I don’t find the
path-1 very descriptive, so I renamed it to
<defs> <rect id=”border” x=”0″ y=”0″ width=”33″ height=”33″ rx=”9″></rect> </defs> <use xlink:href=”#border” … ></use>
id is much nicer and much more descriptive.
Now that we have manually optimised our SVG, you might be wondering: "why bother? Aren't there plenty of automated tools for this?"
Yes, while it's true that there are a host of automated SVG optimisers that will, as the name suggests, automatically optimise the markup of an SVG for you, this can come at a cost.
In some cases, you may find that automated tools can merge all of the SVG layers, making the SVG harder to work with in future.
To quote Sarah Soueidan:
[SVGO] can break the SVG as well as any structure you create in it for animation, for example. I use SVGO sparingly. Ai export is v clean.
You can find an interesting Twitter thread on the topic by Sara Soueidan et al here, which is also the source of the above quote.
And that’s it. Our optimised markup is:
<svg width="39px" height="39px" fill="none" viewBox="-3 -3 39 39" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <defs> <rect id="border" x="0" y="0" width="33" height="33" rx="9"></rect> </defs> <rect fill-opacity="0.6" fill="#FFFFFF" opacity="0.5" x="0" y="0" width="33" height="33" rx="9"></rect> <use xlink:href="#border" stroke="#2D2D2D" stroke-width="6" fill-opacity="0.599999964" fill="#FFFFFF" fill-rule="evenodd"></use> <use xlink:href="#border" stroke="#FF0000" stroke-width="3"></use> <polygon fill="#FF0000" points="19.7149758 19.5157005..."></polygon> </svg>
We’ve managed to reduce the lines of code down to 9; more than a 50% LOC reduction. That’s pretty impressive.
Now, you’re probably wondering what the point of all this cleanup is. Well, clean SVG markup has a myriad of benefits, including:
Maintainability – your SVGs are easier to edit and maintain, without ever opening a graphics editor.
Speed – less code means smaller file sizes, smaller file sizes means a faster loading web page.
Animation – it becomes notably easier to animate SVGs when the markup is clean, optimised and free from redundant tags.
Accessibility – image editors seldom focus on accessibility when generating markup. Having clean markup makes it easier to create accessible markup.
Whilst it is true that an SVG minifier, such as SVGOMG, can do most of the leg work for you, it could damage the quality of the SVG. The choice, and the associated risk, is yours.
Of course, it’s all contextual. Figure out if you have time to optimise your SVGs, and if it’s viable to do so, go ahead! If not, that’s fine too.
I’ve sent a message to my family and delegated my open source projects to my friends. With my last tweet sent, I turn off my laptop, phone, and tablet. My Digital Sabbath begins in 10 minutes: no digital devices for the next month.