Optimizing SVGs For Web Use

The web developer scene is ga-ga for Scalable Vector Graphics (SVG), and it's easy to see why. The files are small, can be rendered at any resolution without loss of quality, and their paths can even be manipulated or animated like any other element on the page. Now that Internet Explorer has gone (unfortunately not with a bang, but a whimper), browser support is ubiquitous.

But you, dear web enthusiast, know that already. This article covers some ways to use SVG better.

Smallify

As with any other kind of static asset, you should be minifying and compressing SVGs before uploading them to your production environment.

SVGs walk the line between Image and Document. Their IANA MIME type is image/svg+xml, but their innards are plain text (XML). This means there is a lot of room for bloat between the lines, so to speak: whitespace, unnecessary code, redundant markup, tautological paths, etc. Rest assured, anything you export from Inkscape or Illustrator is going to be at least twice as large as it needs to be.

De-bloat

The goto tool for SVG optimization is called SVGO (no clue what that stands for). It is typically run via command-line or integrated with a task runner like Grunt as part of the build process. The default options are quite good, so most of the time all you need to do (if executing manually) is run:

# one file
svgo --multipass /path/to/image.svg

# a whole directory
svgo --multipass -f /path/to/happiness

There is also a web-based version living at SVGOMG that works wonders in a pinch.

Compression

Being text, SVGs benefit from Gzip in much the same way your HTML, CSS, and Javascript do. But don't get greedy and skip the de-bloat step; the larger the asset, the longer it will take to unpack and render. Even a modest SVG sprite with a few dozen small icons can add several hundred milliseconds to the gap between "file received" and "file drawn". De-bloating will also reduce the CPU and disk I/O overhead on the server-side.

Fixify

There isn't a lot to an SVG, yet programs like Illustrator have been historically terrible at saving them. We've resorted to writing our own complex wrapper functions for dealing with all the potential wrongness that can be passed off as an SVG, but for assets we have direct control over (such as pieces of a theme), there are three components we always edit by hand:

DOCTYPE

SVGs that are served as individual files, such as via an image's src attribute, should include the correct DOCTYPE declaration at the tippy top:

<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">

However if an SVG is embedded inline, it should not contain a DOCTYPE, only the opening and closing <svg /> tags and the stuff in between.

Any other metadata, tags, comments, etc., should always be removed.

Dimensions

Most* SVGs should have width, height, and viewBox attributes, e.g.

<svg width="20" height="30" viewBox="0 0 20 30" ... >

If yours is missing width or height, grab the third or fourth viewBox numbers respectively. Otherwise if the viewBox is missing, give it a value of 0 0 $width $height. Regardless of what you have heard, w/h and viewBox are not interchangeable; for broadest compatibility you should have all three.

IDs and Classes

Most image programs will generate IDs and classes from your layer and path names, which are almost always going to result in "layer_1", "layer_2", etc. Duplicate IDs can cause all sorts of unexpected issues page-wide, so you should either remove all IDs period (if you don't need to reference them via use, etc.), or randomize them. While recent versions of Illustrator have an export setting that will do this for you, they tend to generate overly long and kinda illegal values. In practice, you can just smash the keyboard to come up with a string of 5 or so characters in length, the first being a letter.

The same applies for classes, which, by the way, you can use instead of inline attributes.

<svg ...>
	<defs>
		<style>
			.xkel3{fill:currentColor;}
		</style>
	</defs>
	<path class="xkel3" ...>
</svg>

If your SVG contains multiple <style> tags (most often the case with autogenerated image sprites), you can collapse the rules into a single tag. If you have overlapping definitions within an SVG, you can merge those too.

Namespace

This is more of a bonus item, not usually necessary, but a helpful workaround in cases where some frontend framework with a bad attitude (like Vue.js) is stripping data from your embedded SVG. As XML, SVGs support XML namespaces. You can namespace tags as follows:

<svg xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" ...>
	<defs>
		<style>...</style>
		<svg:style>...</style>

Using!

SVGs can be added to a web page in several ways:

<!-- directly -->
<svg>...</svg>

<!-- links -->
<img src="https://domain.com/images/image.svg" />
<div style="background-image: url(https://domain.com/images/image.svg);"></div>
<svg><use xlink:href="#some-other-svg-id"></use></svg>
<!-- inline Data-URI -->
<img src="data:image/svg+xml;base64,XXXXX..." />
<?php
// you can dynamically insert an SVG like:
echo file_get_contents('/path/to/image.svg');

Oh, and Security...

Embedding an SVG directly into a document is convenient, removes an extra server request, and gives you more control over its appearance. But don't forget, SVGs are code. Don't go adding them willy-nilly as they can contain malicious payloads.

Always follow best practices:

  1. Never accept SVGs from untrusted parties;
  2. Sanitize the contents ahead of time, stripping illegal or dangerous markup like Javascript or references to external resources;
  3. In dynamic languages like PHP, load the contents to a variable. Don't ever use functions like include() or require(), which will evaluate the contents as PHP code, which, if it contains PHP code, will do PHP things.
Josh Stoik
18 February 2017
Previous Using Let's Encrypt on Debian
Next Climbing MIME Improbable