DEV Community

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>

Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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" }
Enter fullscreen mode Exit fullscreen mode

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>

Enter fullscreen mode Exit fullscreen mode

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>
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

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.

Discussion (14)

pic
Editor guide
Collapse
mpuckett profile image
Michael Puckett

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!

Collapse
jfbrennan profile image
Jordan Brennan Author • Edited

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!

Collapse
mpuckett profile image
Michael Puckett

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!

Collapse
yellow1912 profile image
yellow1912

I have a big navigation tree with hundreds of elements, each with its own icon. For some reason rendering them all at once always block the browser for around 2 seconds. I cannot pin point the issue yet but certainly removing all those icons help (they all belong to 1 big svg sprite that I already included inline). I think switching back to icons may help in this case.

Collapse
jfbrennan profile image
Jordan Brennan Author

How are the SVGs implemented? img tag or svg

Collapse
yellow1912 profile image
yellow1912 • Edited

I'm using the following code to render the individual svg image (from sprite)

<svg class="collapse-icon icon-sm svg-icon icon-color-10">
    <use xlink:href="#general--small--down_2"></use>
</svg>
Enter fullscreen mode Exit fullscreen mode

The main svg image is already loaded inline in the body tag.

Collapse
yellow1912 profile image
yellow1912

I didn't use img tag, I use svg object tag instead I think. I will check my code on Monday. Does it make a difference?

Thread Thread
jfbrennan profile image
Jordan Brennan Author

Hmmm if the main SVG is loaded inline I wonder if it’s large enough of a DOM object to freeze things up for two seconds. Not sure of the internal mechanics, but I’d assume browsers wouldn’t lock up on loading the sprite, but maybe they do? Removing them eliminates your problem?

Thread Thread
yellow1912 profile image
yellow1912

Yup, removing them makes things significantly faster. I haven't been able to find out why yet. Webflow seems to also use tons of svg icons on their navigation tree but does not suffer from the same issue.

Collapse
dannyengelman profile image
Danny Engelman

"None of those SVG approaches come close to the simplicity of font icons."

Comparing <m-icon name="phone"> to a full blown SVG tag, is comparing apples and oranges.

If you apply the same principle it would be <svg-icon name="phone">

If you then get rid of that .woff2 file (always downloading ALL icons) and only include the SVG data for only those icons you use, you get iconmeister.github.io

Collapse
jfbrennan profile image
Jordan Brennan Author • Edited

That’s a cool library, but one of the points I was making was SVG icons require a JavaScript abstraction in order to have a good developer experience whereas font icons do not. If you go SVG I’d agree that Custom Element is the way.

Now what really peaked my interest here is page load. Iconmeister requires all SVG data to be bundled in the script. The comparisons I’ve seen have shown a woff2 of say 100 icons is smaller than 100 icons of SVG data. But there’s some research to be done around loading to see which has better performance, like inlined font icon styles and a cached woff2 vs. inlined script with SVG data. Or CDN everything in both cases. Or ServerWorker cache. Or some combination of those.

Collapse
christophalan profile image
Christopher Alan

Great stuff! Maybe more of a design question but any thoughts on best practices to get icons into a font file? I really like what Apple did with symbols and how the stroke weights match the font weights (not that I need it) but it’s a nice touch. Another thought I had was around “responsive” icons where you show more/less details depending on the sizes being used? I’ve seen .svg examples of this but not sure about icon fonts...

Collapse
jfbrennan profile image
Jordan Brennan Author • Edited

Thanks!

The latter is definitely doable if you mean something like this tympanus.net/codrops/2014/08/19/ma....
For adaptive font icons you just point the icon's content property to more (or less) detailed glyphs with media queries, which is pretty much the same as SVGs hiding/showing different path data with media queries, only SVG icons definitely require a JavaScript abstraction for this.

As for getting icons into a file, I've always been given a production-ready .woff2 file. IcoMoon is one tool I'm aware of that can do this, but I've heard it can be fussy at times. However, the cost of a designer fiddling with IcoMoon for an hour or two is worth the development and maintenance saving of font icons vs. SVG icons. I've been through that process a number of times at two companies where a custom icon set was being created. Font icons were worth it every time.

Collapse
christophalan profile image
Christopher Alan

Thanks! Looking forward to digging into this more.