loading...

SVG icons are not "settled science"

jfbrennan profile image Jordan Brennan Updated on ・8 min read

It seems like every year since SVG support landed in the browser there has been a "It's [current year], You Should Be Using SVG Icons" article and every year I find myself verbally explaining why I think you shouldn't. It's a new year, so I'm gonna get ahead and write this down for the first and last time ;)

Let me first acknowledge SVG's ability to handle fairly intricate multi-colored graphics and their potential for doing really clever things for icons. Definitely use SVGs if you have those use cases. But the vast majority of icon usage - the very requirement that drives these debates - doesn't need or even benefit from those embellishments, so my focus here is on the common icon use case of single monochromatic symbols.

I'll be comparing how users experience SVG icons versus font icons and how developers experience creating, maintaining, and using a set of SVG icons versus font icons.

User experience

Users cannot tell the difference between SVG icons and font icons because they are visually identical on screen. Put two of the same "phone" icons next to each other, one using SVG and one using font, and there will be no visual difference. Change the color - no difference. Zoom in or out - no difference. They are visually identical.

The only exceptions between SVG and font icon visual parity are when IE fails to render SVGs nicely if the user zooms in really far. It's apparently an IE edge case and IE is pretty much gone now. I also once saw Chrome crop 1 pixel off just one font icon out of a set of over 100 icons. Never figured it out. I guess this is a tie, but I don't think either of these browser bugs are worth consideration.

As for accessibility, there's nothing in an SVG element that's useful to screen readers. Both SVG icons and font icons need to use the same ARIA attributes: aria-label="phone icon" or aria-labelledby.

One area where there could be a small difference in user experience is page load speed. SVG carries extra weight, especially if done with an abstraction like React components, which is common for SVG icons and I'll show why below. Font icons require the least amount of code and resources and those resources cache better than some of the various SVG approaches, so font icons have potential for faster page load times compared to SVG. Your app likely has bigger performance gains to be found elsewhere, but if you're hyper-optimizing go with font icons to shave off some ms.

So, when considering user experience SVG icons and font icons are visually identical and equally accessible. Font icons do have potential for faster page loads than SVG.

Developer experience

Let's just get right to the code!

SVG icon (scroll to the right to see all its glory)

<svg class="icon icon-phone" viewBox="0 0 12 16" version="1.1" width="12" height="16" aria-hidden="true">
  <path fill-rule="evenodd" d="M11 11.28V5c-.03-.78-.34-1.47-.94-2.06C9.46 2.35 8.78 2.03 8 2H7V0L4 3l3 3V4h1c.27.02.48.11.69.31.21.2.3.42.31.69v6.28A1.993 1.993 0 0 0 10 15a1.993 1.993 0 0 0 1-3.72zm-1 2.92c-.66 0-1.2-.55-1.2-1.2 0-.65.55-1.2 1.2-1.2.65 0 1.2.55 1.2 1.2 0 .65-.55 1.2-1.2 1.2zM4 3c0-1.11-.89-2-2-2a1.993 1.993 0 0 0-1 3.72v6.56A1.993 1.993 0 0 0 2 15a1.993 1.993 0 0 0 1-3.72V4.72c.59-.34 1-.98 1-1.72zm-.8 10c0 .66-.55 1.2-1.2 1.2-.65 0-1.2-.55-1.2-1.2 0-.65.55-1.2 1.2-1.2.65 0 1.2.55 1.2 1.2zM2 4.2C1.34 4.2.8 3.65.8 3c0-.65.55-1.2 1.2-1.2.65 0 1.2.55 1.2 1.2 0 .65-.55 1.2-1.2 1.2z"></path>
</svg>

Wow! No developer would dare code one of those let alone a whole set. SVG icons immediately present an engineering challenge of figuring out how to generate and manage these complicated HTML snippets and get them onto the page. There's several approaches and each has their trade-offs.

One way is to put each SVG icon snippet into a small partial template file and import them where needed. This is not difficult to do when rendering server-side, but it gets more complicated when trying to package and render these client-side. Abstracting them into JavaScript components, usually with React, is the status quo. This approach adds a dependency and increased payload, about 40kb in the case of React, which definitely impacts page speed. I'll dig into this this approach later because it is the most common.

It is possible to avoid writing all that path data and need for JavaScript by including an actual .svg file with <img src="phone.svg"> or background-image: url("phone.svg"). Those work but introduce a bad UX side-effect because each icon loads separately, which means they pop in randomly on the page as they complete their download. You can get around that by going with the SVG sprite approach, but now the file has to be a bunch of symbol elements, which is neither here nor there because you have to set up a build pipeline to generate this file for you. With that approach you can now write less code than above. Something like:

<svg class="icon icon-phone" viewBox="0 0 100 100">
  <use xlink:href="icons.svg#icon-phone"></use>
</svg>

But even that leaves a lot of people reaching for React or some other JavaScript abstraction. There really is no all-around winning approach with SVG icons. Every choice leaves you hurting the developer experience or the user experience or both.

Ok, now it's icon fonts' turn.

Icon font

<m-icon name="phone"></m-icon>

That's really simple. Developers could code those all day. Server-side or client-side render there's no difference.

This may look a little different than the class-based implementations of icon fonts you've seen or worked with in the past. This approach uses a custom HTML tag and there's no magic to it! No dependencies, no JavaScript whatsoever, no smoke and mirrors. It's plain CSS and a font file:

@font-face {
  font-family: "m-icons";
  src: url("m-icons.woff2") format("woff2");
}

m-icon {
  font-family: "m-icons";
  display: inline-block;
}

m-icon[name="phone"]:before { content: "\e911" }

Less is more

None of those SVG approaches come close to the simplicity of font icons. Comparing the most simple SVG option again really shows where the developer experience favors icon fonts:

<svg class="icon icon-phone" viewBox="0 0 100 100">
  <use xlink:href="icons.svg#icon-phone"></use>
</svg>

vs.

<m-icon name="phone"></m-icon>

For the developer, icon font is less to code, less to get wrong, less to test, and less to remember.

Now, it is technically possible to get a similar developer experience with SVG, at least for developers using the icons not creating them, but again it requires a heavy abstraction. Let me explain.

SVG icons + React

The first SVG code example above was taken from GitHub's website where, unsurprisingly, they don't actually require their developers to write all that SVG code. They've encapsulated it in a React component. This makes the React developer experience better:

import React from 'react'
import Octicon, {Phone} from '@primer/octicons-react'

class MyApp extends React.Component {

  ...

  render() {
    return (
      <div>
        <Octicon icon={Phone}/>
      </div>
    );
  }
}

For devs using these SVG icons that's certainly an improvement in regards to the markup. But there are problems with this approach:

Limited compatibility
These SVG icons are now React components so they do not work anywhere except in a React app. The trade-off is a good icon API for extremely limited compatibility. Two products can't share these icons unless they've both been built with React.

Dependencies
This approach introduces hard dependencies even though it's just simple icons.

The first dependency is on JavaScript itself. Browsers with no JavaScript is not a valid use case imo, but there are two more environments where you won't have JavaScript and that's servers and email clients.

Server-side rendering these is not possible unless you have Node. I personally think Node is the best for web application servers, but icons that can't also be rendered by Java or Ruby or Python or C# web stacks is really bad.

Email is notoriously difficult to code for, so there is no silver bullet for sure, but icon fonts do work in some email clients and icon fonts implemented with custom HTML tags still work in some of those (Apple's Mail app and Outlook last time I tested, but no Gmail). Basic SVG icons - image file or raw HTML - also have very poor support, but SVG icons with a JavaScript abstraction have no support at all. So the little email compatibility SVG icons have are lost completely when implemented with React.

The second dependency is on React itself. Developers need to learn the framework in order to create the icon component and other developers will have to learn the framework in order to use them. Dependencies like these also have to be kept up to date. There's also the challenge of common dependencies within a project. If the icon set was built with React version 16, but the consuming app uses an older version you will likely run into compatibility headaches. If the app moves away from React to something else then there's also the challenge of making two frameworks coexist.

The third dependency introduced is the tooling required to make SVG icons possible. The moment React or any other JavaScript library is introduced, you create a dependency on build tools and a need to write tests. None of that development overhead exists with font icons (remember: it's just plain CSS and a font file).

SVG icons are a lot of work. Balancing their trade-offs is a pain and so most teams go the React route and just try to forget about the over-engineered mess. The whole thing can be avoided with font icons, which have even more benefits that haven't been discussed so far.

Other benefits of font icons

Font icons have some nice benefits that SVG icons don't. These are:

  • Automatically matches text color
  • Automatically matches text size
  • Automatically matches line height

That's a very convenient behavior that most often results in the desired effect. But if not, or if there comes a need to add embellishments, you can do a lot with font icons by using all kinds of CSS properties: text shadow, transforms, animation, clipping/masking, opacity, stroke, and more.

Summary

Font icons and SVG icons offer the same visual user experience, but font icons have a slight edge in performance. The two are equally accessible.

The developer experience of creating, maintaining, and using font icons is superior to SVG because it only takes some plain CSS and the font file to create them and a single HTML element to use one.

Posted on Jan 3 by:

jfbrennan profile

Jordan Brennan

@jfbrennan

Building great products

Discussion

markdown guide
 

I was skeptical — I think in general SVG is the way to go — but you bring up one important use case:

Abstracting them into JavaScript components, usually with React, is the status quo. This approach adds a dependency and increased payload, about 40kb in the case of React, which definitely impacts page speed

That’s true! If you are building a bare-bones site without much interactivity, React would definitely be too much.

You could probably get away with similar markup with a native web component (just a few lines of JavaScript).

Nice work thinking through it from all angles!

 

As much as I love WC it’s sub-optimal because the icons won’t work with static pages or server rendered pages or email templates. WC (really just Custom Elements) also introduce the need for another build step or more config, requires test coverage, and probably needs a polyfill when using.

Skip all of it with a simple custom tag+attribute!

 

I don’t think you need a build step to do what I was suggesting for static sites but I get where you’re coming from — email templates would certainly be a great place to use icon fonts!