Cover image for Icon systems for the web - an in-depth guide

Icon systems for the web - an in-depth guide

adrianbdesigns profile image Adrian Bece Updated on ・6 min read

Icons are commonly used elements on the web. They are universal, instantly recognizable, can be very appealing, draw attention, and (if used correctly) provide a great user experience.

We have quite a few options when implementing icons on the web:

  • Icon Spritesheet
  • Icon font
  • Inlined SVG
  • SVG as an image element

Some of them are more commonly used today, like SVG elements and Icon fonts. In this article, we're going to dive deep into each approach of implementing Icons on the web and see which approach is the best in terms of performance, accessibility, styling options and browser support.

Icon Spritesheet

We create an Icon Spritesheet by combining smaller image (icon) files into a single, larger file. We need to use CSS background-image, background-size and background-position to display the image from the spritesheet.

Icon spritesheet example

Spritesheet example

We can use SVG spritesheet to ensure that icons look great on various displays (regular and retina) in combination with PNG spritesheet as a fallback for older browsers. We can use a JavaScript library like Modernizr to detect if the SVG is supported on the user's browser and provide a PNG fallback if SVG is not supported.

Let's create an accessible icon:

<span aria-hidden="true" class="icon icon--email"></span><span class="hidden--visually">Send me an Email</span>>

Spritesheet CSS example (Can be generated or manually added):

.icon {
  background-image: url('../images/spritesheet.svg');
  background-repeat: no-repeat;
  display: inline-block;
  width: 64px;
  height: 64px;

.no-svg .icon {
  background-image: url('../images/spritesheet.png');

.icon--email {
  width: 64px;
  height: 64px;
  background-position: 0px 0px;

If you are wondering about the hidden--visually CSS class and how to accessibly hide content, I've explained it in more detail in one of my previous articles.

Let's review the pros and cons of using icon spritesheet.


  • Process of adding icons can be easily automated
  • Image optimization techniques can reduce the spritesheet file size
  • Works well on all displays (when SVG spritesheet is used)
  • Great browser support (when PNG fallback is used)
  • Reduced number of requests (useful when HTTP/2 is not used)


  • Not accessible out of the box. Accessibility needs to be manually added with additional HTML elements.
  • Poor styling option
  • All variations of the icon need to be added as a separate element in the spritesheet
  • Conflicts can happen if multiple people add new icons to it at the same time
  • No performance benefits when using HTTP/2


  • Sprite Cow - Online spritesheet image and CSS generator
  • PostCSS-lazysprite - PostCSS plugin for automating spritesheet image and CSS generation
  • gulp-svg-sprite - Gulp plugin for automating spritesheet image and CSS generation

Icon font

Instead of combining our icon image files into a single image file, we can generate font files that will contain our icons. Browsers will treat them as text and they can be easily customized with text styles.

There are numerous tools that make generating icon font files and a CSS file very easy and manageable. Generated CSS file can look something like this:

/* Define font icon font family */
@font-face {
    font-family: 'myIconFont';
    src: url('/path/to/myIconFont.ttf?r9c57c') format('truetype'),
        url('/path/to/myIconFont.woff?r9c57c') format('woff'),
        url('/path/to/myIconFont.svg?r9c57c#myIconFont') format('svg');
    font-weight: normal;
    font-style: normal;

/* Define icon class that sets font family and shared font styles */
.icon {
    font-family: 'myIconFont';
    speak: none;
    font-style: normal;
    font-weight: normal;
    font-variant: normal;
    text-transform: none;
    line-height: 1;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;

/* Define individual icon class that inserts the icon as a character in pseudo-element */
.icon--email::before {
    content: '\e900';

We can use the similar markup to crate an accessible icon:

<span aria-hidden="true" class="icon icon--email"></span><span class="hidden--visually">Send me an Email</span>>

Let's review the pros and cons of using font icons.


  • Good optimization options
  • Easily editable and generated with various tools
  • Great browser support
  • Really easy and convenient to use


  • Font anti-aliasing might cause an issue with icon rendering
  • Nothing is displayed while font file is downloading and loading
  • Can easily be overridden if a user is using specific font or style override
  • Not accessible out of the box. Accessibility needs to be manually added.


  • icomoon - manage and generate icon font file & CSS
  • fontello - manage and generate icon font file & CSS
  • icon-font-generator - NPM plugin for generating icon font from SVG icons

Inline SVG icons

Instead of including some file (spritesheet or icon font) and adding an icon into our markup with CSS, we can directly insert SVG data into HTML document and the browser will parse and display the SVG element. Inline SVG elements are highly customizable because we can even style the individual SVG element.

Let's take a look how an accessible icon using inline SVG looks like:

<svg labelledby="titleId descId" role="group">
    <title id="titleId">Example title</title>
    <desc id="descId">Long description explaining this example</desc>

    <!-- SVG icon code -->

We are using labelledby="titleId descId" with title and desc SVG elements to make the element accessible to an assistive device. It's also important to note that role="presentation" should be used on graphical parts of the SVG elements that should be ignored by an assistive device.

Let's review the pros and cons of using inline SVG icons.


  • Loads with HTML document with no additional load time
  • No additional HTTP requests
  • Good accessibility support with no additional HTML elements needed
  • Looks great on various screens
  • Best in terms of styling options (can even style elements within the <svg> element)


  • Complicates HTML document markup (depends on a framework that is used)
  • Hard to manage and maintain (depends on a framework that is used)
  • Icons are not cached
  • Some older (and less used) browsers are not supported

Some frameworks make managing and maintenance of icons easier. For example, Webpack can convert all import MyIcon from "/path/to/myIcon.svg" into an inline SVG on a production build. So for dev, we have a simple markup that can be easily maintained.

SVG in image elements

Instead of having an entire SVG markup in HTML, we can keep the icons as separate files and use <img> HTML element to include it in our markup. This enables us to have a simple and maintainable markup and small file sizes. We can also use native accessibility features of the element for our icons.

Let's create an accessible icon using this approach:

<img src="email.svg" alt="Send me an email">

We can implement PNG fallback for older browsers by using srcset:

<img src="email.png" alt="Send me an email" srcset="email.svg">

Browsers that support srcset also support SVG elements and they will automatically load the SVG image. Browsers that do not support it will load the PNG fallback.

Let's review the pros and cons of using SVG in image elements.


  • Simple markup
  • Built-in simple accessibility options with alt tag
  • Good browser support (if PNG fallback is used)
  • Image file size can be optimized
  • Image files can be cached (once downloaded)


  • Poor styling options
  • Each variation of the icon (different color, for example) should be in a separate file
  • Each icon requires a server request (if not cached)

Bonus: Image optimization

I've mentioned how image file size optimization can improve download and loading times. If you are interested in learning more about image file size optimization, check out this article.

Community poll

What is your favorite go-to choice when implementing icons on the web? Let me know by submitting your vote on the following Twitter poll.

These articles are fueled by coffee. So if you enjoy my work and found it useful, consider buying me a coffee! I would really appreciate it.

Buy Me A Coffee

Thank you for taking the time to read this post. If you've found this useful, please give it a ❤️ or 🦄, share and comment.

Posted on by:

adrianbdesigns profile

Adrian Bece


React, Frontend, Magento 2 certified developer. Magento PWA Studio contributor. Rock and metal music fan. Reads Dune, sci-fi novels and Calvin & Hobbes. Creates amazing interfaces @ prototyp.digital


markdown guide

Hey Adrian, for about 4 years I'm all in for the inline SVG solution. I tried almost each of your methods for years, non really stood out to me, especially in case of maintaining.

If a new icon needs to be added to the app, the whole png sprite needs to be redone, same with icon fonts.

Since I'm using Vue and React I do components for each icon and import them on the fly. Before Vue and React I did SVG sprites, as it made my code still maintainable and readable to other developers.


Hi Dean, thanks for sharing your opinions and experience.

It was pretty much the same thing for me. I've also started with spritesheets and icon fonts and move onto the Inline SVG solutions once I started working with React.

I've noticed that browser requirements and frameworks dictated the approach I took.


I am heavily leaned towards inline svg, didn't use other aproaches much lately.

I also like to optimize svg with svgomg and remove unnecessary stuff.

If icons are single colored, svg path d of every icon can be stored in single object and then exported.

This is my favorite way of handling Icons. Just

<Icon d={Icons.EDIT} />

is enough!


Hi Ivan, thanks for sharing your approach. Frameworks like React and Vue really improved the maintainability and flexibility of icon systems. I also handle Icons in the same way on my React projects.


Hi mate! Nice article... I just wanted to mention that we're using postcss-inline-svg plugin and it gives us the ability to change fill and stroke colors using CSS vars with color functions, it's a good solution just for the icons. But, there's one issue though, it's not DRY, so if you want to change icon fill on hover you have to duplicate icon. But, it's clean and work's like a charm.


Hi Mihael, thank you for your input. I will check out the plugin you've mentioned. I've noticed that there is a noticeable tradeoff in every icon system: Caching, accessibility, customization, DRY as in your case, etc. I guess it depends on priorities on the project and design itself.


Really very useful information, Thank you very much Adrian!!!
One more thing, sprite are recommended for website optimizations while we audit our website.

Keep publishing cool stuff :)