Creating a FontAwesome bundle with only the icons you use

 
 
  • Gérald Barré

FontAwesome contains thousands of icons, but you often use a very small subset of those icons. For instance, I only use 15 icons on this website. So, why do you want your visitors to download hundreds of icons they won't use. One solution is to create a custom bundle that contains only the icons you use. You can reduce the bundle size from a few hundred kB to a few kB (6kB on this website).

#Minifying FontAwesome using JavaScript modules

If you are using FontAwesome with ES6 or Node.js you can use Font Awesome (JavaScript) SVG Core.

JavaScript
import { library, dom } from '@fortawesome/fontawesome-svg-core'
import { faUserAstronaut } from '@fortawesome/free-solid-svg-icons'

// We are only using the user-astronaut icon
library.add(faUserAstronaut)

// Replace any existing <i> tags with <svg> and set up a MutationObserver to
// continue doing this as the DOM changes.
dom.watch()

Then, using a tool such as Webpack that provides tree shaking to bundle your application, only the used icons will be in the final package.

#Minifying FontAwesome using a custom application

In my case, I prefer generating a JavaScript file without any extra things such as the MutationObserver. Also, I would like to integrate icons from other icon sets.

First, you have to download the zip file that contains all the icons as svg files, and keep only the icons you want:

Then, you can create the C# application:

  1. Download .NET SDK: https://dotnet.microsoft.com/download

  2. Create a new application and add the needed NuGet references

    Shell
    dotnet new console --name FontAwesomeBundler
    cd FontAwesomeBundler
    dotnet add package Newtonsoft.Json
  3. Replace the Program.cs file with the following content:

    C#
    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.IO;
    using System.Text;
    using System.Xml.Linq;
    using Newtonsoft.Json;
    
    namespace FontAwesomeBundler
    {
        internal class Program
        {
            private static void Main(string[] args)
            {
                var ns = XNamespace.Get("http://www.w3.org/2000/svg");
    
                var sb = new StringBuilder();
                sb.Append("(function(){");
    
                var icons = new Dictionary<string, Dictionary<string, string[]>>(StringComparer.Ordinal);
    
                foreach (var dir in Directory.GetDirectories(args[0]))
                {
                    var currentDict = new Dictionary<string, string[]>(StringComparer.Ordinal);
                    icons[Path.GetFileName(dir)] = currentDict;
                    foreach (var file in Directory.GetFiles(dir, "*.svg"))
                    {
                        var doc = XDocument.Load(file);
                        var className = Path.GetFileNameWithoutExtension(file);
                        var viewBox = doc.Root.Attribute("viewBox").Value;
                        if (!viewBox.StartsWith("0 0 ", StringComparison.Ordinal))
                            throw new Exception($"Expecting viewbox to start with '0 0 ' for '{className}'");
    
                        viewBox = viewBox.Substring("0 0 ".Length);
    
                        var path = doc.Root.Element(ns + "path").Attribute("d").Value;
                        var size = viewBox.Split(' ');
                        var width = int.Parse(size[0], CultureInfo.InvariantCulture);
                        var height = int.Parse(size[1], CultureInfo.InvariantCulture);
                        var ratio = width / height;
                        var ratioClass = ratio == 1f ? "16" : (ratio == 1.25f ? "20" : "14");
                        currentDict.Add(className, new[] { viewBox, path, Math.Ceiling(width / (double)height * 16d).ToString(CultureInfo.InvariantCulture) });
                    }
                }
    
                sb.Append("var icons = " + JsonConvert.SerializeObject(icons, Formatting.Indented) + ";");
    
                sb.Append(@"function i2svg() {
        var ns = 'http://www.w3.org/2000/svg';
        var groups = Object.keys(icons);
        for (var i = 0; i < groups.length; i++) {
            var groupName = groups[i];
            var iconNames = Object.keys(icons[groupName]);
            for (var j = 0; j < iconNames.length; j++) {
                var iconName = iconNames[j];
                var elements = document.querySelectorAll('i.' + groupName + '.fa-' + iconName);
                for (var k = elements.length - 1; k >= 0; k--) {
                    var iconData = icons[groupName][iconName];
                    var svg = document.createElementNS(ns, 'svg');
                    svg.setAttribute('viewBox', '0 0 ' + iconData[0]);
                    svg.setAttribute('class', 'fa fa-' + iconData[2]);
                    svg.setAttribute('aria-hidden', 'true');
                    var path = document.createElementNS(ns, 'path');
                    path.setAttribute('d', iconData[1]);
                    path.setAttribute('fill', 'currentColor');
                    svg.appendChild(path);
    
                    elements[k].parentNode.replaceChild(svg, elements[k]);
                }
            }
        }
    }
    var raf = window.requestAnimationFrame;
    raf ? raf(function () { window.setTimeout(i2svg, 0); }) : window.addEventListener('load', i2svg);
    ");
    
                sb.Append("})();");
                File.WriteAllText(@"fontawesome-custom.js", sb.ToString());
            }
        }
    }
  4. Run the application to generate the bundle:

    Shell
    dotnet run <Path to the icons folder>

You now have a custom JavaScript file that will replace the <i class="fas fa-rss"></i> tags by <svg>...</svg> tags. Finally, you need to add the following CSS rules to display the icons on the page. This does not include all the rules from FontAwesome because I don't need all of them for this website. For instance, animations (fa-spin or fa-pulse) are not available, but you are free to copy them from the original stylesheet.

CSS
.fa {
  display: inline-block;
  font-size: inherit;
  height: 1em;
  overflow: visible;
  vertical-align: -.125em;
}

.fa-9 {
  width: 0.5625em;
}

.fa-12 {
  width: 0.75em;
}

.fa-14 {
  width: 0.875em;
}

.fa-16 {
  width: 1em;
}

.fa-18 {
  width: 1.125em;
}

.fa-20 {
  width: 1.25em;
}

#Conclusion

The loading time of FontAwesome on this website is much faster because the bundle only contains the required icons. So the downloading time, parsing time, and evaluation time are faster. It also reduces the memory needed to display the page. It has been a very good change on this website in terms of performance!

Do you have a question or a suggestion about this post? Contact me!

Follow me:
Enjoy this blog?Buy Me A Coffee💖 Sponsor on GitHub