How does Iconify work?

This is a long article explaining stuff in details. Shorter less technical explanation is available on about page.

Iconify is not a simple SVG framework. It has multiple parts:

Why is it so complex?

Because of one major feature that sets it apart from fonts and other SVG frameworks: load on demand. Also Iconify was made to be flexible to allow anyone to use it with custom icon sets.

Load on demand is what makes Iconify so complex and also so powerful. Load on demand cannot be achieved without API that serves icons. Server cannot just send entire icon collections, like other SVG frameworks do, because it would results in many megabytes of data and slow down page loading. Instead server only sends data for icons that are used on page, making it possible to have unlimited choice of icons.

Graph below should help you understand interaction between browser and API.


  • Script looks for icon placeholders.

  • Script retrieves icon names from placeholders.

  • Script connects to Iconify API and retrieves SVG data for all icons found on page.

    Iconify API is hosted on network of servers spread across the world, so data would load in fraction of a second from any location.

  • Script replaces placeholders with SVG.

How does each part work?

Client side script

Client side script does all its magic in visitor's browser.

Script is very small, only about 21kb uncompressed. It is delivered from CloudFlare CDN, so it loads very quickly from anywhere in the world.

Basic functionality

Usually Iconify parses document only once and does it in this order:

  1. Scans document for icon placeholders: <span class="iconify" data-icon="mdi-home"></span>
  2. Connects to Iconify API to get data for all icons found on page (or retrieves data from cache).
  3. Replaces placeholders with SVG icons: <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" width="1em" height="1em" style="vertical-align: -0.125em;-ms-transform: rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg);" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24" class="iconify" data-icon="mdi-home"><path d="M10 19.998v-6h4v6h5v-8h3l-10-9-10 9h3v8h5z" fill="currentColor"></path></svg>

AJAX, React and Angular friendly!

After initial page load, Iconify continues to watch page for changes.

Whenever new elements are added to DOM, it scans DOM again for icon placeholders.

Because of that script works very well with dynamic content, such as AJAX forms, React or Angular applications.

To swap icon simply replace SVG or its placeholder with new placeholder. To optimize loading of dynamically added icons, consider pre-loading them during page load, so they would load in bulk.

Scripting

You can use Iconify functions in scripts.

Iconify functions can:

  • Check if icons have been loaded, there is event you can subscribe to to be notified when new icons have been loaded.
  • Add custom API entries or change existing entries, so you can use Iconify with your own API (to keep everything on your server and/or to use custom icons).
  • Functions to write plugins to support custom icon syntax. Default repository includes plugins for several fonts, such as FontAwesome plugin that supports <i class="fa fa-home" /> syntax.
  • Retrieve icon data, so you can use it in your code.

Because of load on demand feature and icons being loaded in bulk (see server side API section below), usual browser caching is not enough. However Iconify has multiple caching mechanisms to reduce bandwidth usage:

  • Parameters for API URLs are sorted, so even if icons are found on page in different order, API URL can be the same, enabling basic browser caching.
  • Loaded icons are stored in localStorage and sessionStorage. By default sessionStorage is enabled, localStorage is disabled. You can toggle it in Iconify configuration.
  • Iconify has ability to pre-load icons. It can be used to bundle icons with custom scripts to serve them faster from your own server. This method is used on this website.

Combination of all those methods assures that icons are not loaded more than once from API, making icon rendering almost instant and reducing page load.

Server side API

API is integral part of Iconify. Its part of what makes Iconify unique.

API is a simple Node.js or PHP script running on server. All icons are stored on server. API processes client request, finds icons client is looking for, returns them as JSON or JSONP data.

Because of Iconify API it is possible to serve thousands of icons without wasting visitor's bandwidth. Other SVG frameworks and glyph fonts load entire collections, Iconify loads only icons that are used on page. Client script decides what icons to load, API sends data for those icons to client, client replaces placeholders with SVG.

Sample request

Sample client request: http://api.iconify.design/fa.js?icons=angle-right,bars,bell-o,home

In this example request has "js" extension, which means response will be JSONP, not JSON. Iconify uses JSONP to bypass origin issues. JSONP is basically JSON response served as JavaScript file wrapped in callback function.

API response:

Iconify._loaderCallback({
  "prefix": "fa",
  "icons": {
    "angle-left": {
      "body": "<path d=\"M595 288q0 13-10 23L192 704l393 393q10 10 10 23t-10 23l-50 50q-10 10-23 10t-23-10L23 727q-10-10-10-23t10-23l466-466q10-10 23-10t23 10l50 50q10 10 10 23z\" fill=\"currentColor\"/>",
      "width": 608,
      "height": 1280,
      "inlineTop": -256,
      "inlineHeight": 1792,
      "verticalAlign": -0.143
    },
    "bars": {
      "body": "<path d=\"M1536 1088v128q0 26-19 45t-45 19H64q-26 0-45-19t-19-45v-128q0-26 19-45t45-19h1408q26 0 45 19t19 45zm0-512v128q0 26-19 45t-45 19H64q-26 0-45-19T0 704V576q0-26 19-45t45-19h1408q26 0 45 19t19 45zm0-512v128q0 26-19 45t-45 19H64q-26 0-45-19T0 192V64q0-26 19-45T64 0h1408q26 0 45 19t19 45z\" fill=\"currentColor\"/>",
      "height": 1280,
      "inlineTop": -256,
      "width": 1536,
      "inlineHeight": 1792,
      "verticalAlign": -0.143
    },
    "bell-o": {
      "body": "<path d=\"M848 1696q0-16-16-16-59 0-101.5-42.5T688 1536q0-16-16-16t-16 16q0 73 51.5 124.5T832 1712q16 0 16-16zm-666-288h1300q-266-300-266-832 0-51-24-105t-69-103-121.5-80.5T832 256t-169.5 31.5T541 368t-69 103-24 105q0 532-266 832zm1482 0q0 52-38 90t-90 38h-448q0 106-75 181t-181 75-181-75-75-181H128q-52 0-90-38t-38-90q50-42 91-88t85-119.5 74.5-158.5 50-206T320 576q0-152 117-282.5T744 135q-8-19-8-39 0-40 28-68t68-28 68 28 28 68q0 20-8 39 190 28 307 158.5T1344 576q0 139 19.5 260t50 206 74.5 158.5 85 119.5 91 88z\" fill=\"currentColor\"/>",
      "width": 1664,
      "height": 1792,
      "inlineTop": 0,
      "inlineHeight": 1792,
      "verticalAlign": -0.143
    },
    "home": {
      "body": "<path d=\"M1408 768v480q0 26-19 45t-45 19H960V928H704v384H320q-26 0-45-19t-19-45V768q0-1 .5-3t.5-3l575-474 575 474q1 2 1 6zm223-69l-62 74q-8 9-21 11h-3q-13 0-21-7L832 200 140 777q-12 8-24 7-13-2-21-11l-62-74q-8-10-7-23.5T37 654L756 55q32-26 76-26t76 26l244 204V64q0-14 9-23t23-9h192q14 0 23 9t9 23v408l219 182q10 8 11 21.5t-7 23.5z\" fill=\"currentColor\"/>",
      "width": 1664,
      "height": 1312,
      "inlineTop": -224,
      "inlineHeight": 1792,
      "verticalAlign": -0.143
    }
  },
  "aliases": {
    "angle-right": {
      "parent": "angle-left",
      "hFlip": true
    }
  }
});

Instead of returning SVG images, Iconify returns SVG content and attributes. This allows script to manipulate data when creating SVG, such as rotating, flipping, resizing and scaling icon based on icon placeholder attributes.

Icons are loaded in bulk to reduce loading speed. This little feature makes massive difference in performance.

API can also generate SVG images that can be used as backgrounds in stylesheet, but it should be avoided because this way each icon is loaded separately, increasing loading times:

http://api.iconify.design/mdi-home.svg?color=darkred&height=96

Hosting

If you want to setup your own instance(s) of Iconify API to serve custom icons or to keep everything on your servers, code for Iconify API is available on GitHub:

Node.js version is faster (by about 25-50ms) because it doesn't need to load data on each request. However it is a bit harder to setup. PHP version is very easy to use, so unless you are comfortable with running Node.js application on server you should use PHP version of API.

JSON format and icon cleanup

Iconify doesn't serve icons in same form as they were exported from image editing software. Icons need to be cleaned up and modified before they can be used with Iconify.

What exactly is changed?

  • Clean up process removes all unnecessary junk that image editors leave behind, stripping icon to instructions that create vector shapes.
  • Icons are optimized using SVGO library.
  • Monotone icons are usually designed as black icons. If such icons are inserted into document, they remain black. However in Iconify monotone icons can change color to current text color, just like glyph fonts. This is done by changing fill and stroke color from black to currentColor. If icon has no fill color, Iconify Tools add it.
  • Icon is split into content and viewBox and stored in collection.
  • Duplicate icons that are simple transformations of each other, such as left and right arrow are treated as one icon + alias. You can see example in API response code sample above. In that example "angle-right" is alias of "angle-left" with "hFlip" attribute set to true, which means icon is flipped horizontally. Such icons are often used together on same page, so this minor optimization saves bandwidth.

For example this is original icon for "bpmn-task":

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
   xmlns:dc="http://purl.org/dc/elements/1.1/"
   xmlns:cc="http://creativecommons.org/ns#"
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
   xmlns:svg="http://www.w3.org/2000/svg"
   xmlns="http://www.w3.org/2000/svg"
   version="1.1"
   id="svg3891"
   height="2048"
   width="2048">

  <defs
     id="defs3893" />

  <metadata
     id="metadata3896">

    <rdf:RDF>
      <cc:Work
         rdf:about="">

        <dc:format>image/svg+xml</dc:format>
        <dc:type
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />

        <dc:title></dc:title>
      </cc:Work>
    </rdf:RDF>
  </metadata>
  <g
     transform="translate(0,995.63783)"
     id="layer1">

    <g
       id="layer1-6"
       transform="matrix(96.752895,0,0,96.752895,55.328158,-100816.34)">

      <path
         id="rect3804"
         d="m 3.9882812,1034.5352 c -1.8058997,0 -3.27539058,1.4694 -3.27539058,3.2753 l 0,8.961 c 0,1.8059 1.46949088,3.2754 3.27539058,3.2754 l 12.0468748,0 c 1.8059,0 3.275391,-1.4695 3.275391,-3.2754 l 0,-8.961 c 0,-1.8059 -1.469491,-3.2753 -3.275391,-3.2753 l -12.0468748,0 z m 0,1.0332 12.0468748,0 c 1.251185,0 2.242188,0.991 2.242188,2.2421 l 0,8.961 c 0,1.2512 -0.991003,2.2422 -2.242188,2.2422 l -12.0468748,0 c -1.2511848,0 -2.2421874,-0.991 -2.2421874,-2.2422 l 0,-8.961 c 0,-1.2511 0.9910026,-2.2421 2.2421874,-2.2421 z"
         style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.03356075;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />

    </g>
  </g>
</svg>

As you can see it has massive amount of useless junk that serves no purpose.

After clean up process and converting it to JSON, this is result:

{
  "prefix": "bpmn",
  "icons": {
    "task": {
      "body": "<rect width=\"17.563\" height=\"14.478\" x=\"1.23\" y=\"1035.052\" rx=\"2.759\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.034\" stroke-linecap=\"round\" stroke-linejoin=\"round\" transform=\"matrix(96.7529 0 0 96.7529 55.328 -99820.702)\"/>",
      "width": 2048,
      "height": 2048
    }
  }
}

Compact, clean, easy to manipulate.

However that's not all. There are also tools to extract icons from web fonts and various other formats. All tools are designed for Node.js. If you are planning to use Iconify with custom icon sets, you need to learn how to work with Node.js and understand how JavaScript promises work.

Tools for converting and cleaning up custom icon sets are available on GitHub.