Better icon components with Icomoon, React and Raven

We like icon fonts over here at Etch. Easy to use, easy to color and pretty cross-browser friendly (except poor Opera mini).

Gav McKenzie
Author
Gav McKenzie
Published
Oct 3, 2016
Topics
How-to, Industry, Engineering

The standard approach is to use a <span> or an <i> tag with some classes on it to display the icon you want. Icomoon also offer multi-colored icons by using a couple of child elements within the icon.

<i class=”icon icon-brisket”></i>
<span class=”icon icon-brisket”></span>
<i class=”icon icon-bbq”>
<span class=”path1"></span>
<span class=”path2"></span>
</i>

This is all great, but it leaves the management of the icons up to people and people are fallible. How could we add more security to our icon usage?

Icomoon

Icomoon exports a JSON file with your icon font that contains a wealth of information about the icons. Most notably for us: tags (used for classes) and multicolored.

“icon”: {
“paths”: [
“M549.394 708.614v-300.967h-132.047v50.287h46.889v250.68h-46.889v50.296h178.927v-50.296z”,
“M460.996 269.819h88.723v88.733h-88.723v-88.733z”,
“M506.815 186.374c179.56 0 325.635 146.076 325.635 325.626s-146.085 325.617–325.635 325.617c-179.55 0–325.635–146.066–325.635–325.617s146.085–325.626 325.635–325.626zM506.815 93.089c-231.364 0–418.911 187.547–418.911 418.911s187.547 418.901 418.911 418.901c231.364 0 418.901–187.538 418.901–418.901s-187.538–418.911–418.901–418.911v0z”
],
“isMulticolor”: false,
“isMulticolor2”: false,
“grid”: 0,
“tags”: [
“topic_overview”
]
},

This gives us a set of data we can check our icon request against at runtime, because we’re using…

React

As the Icon component is loaded with a request for an icon, we check to see if that icon exists in the JSON. If it does, everything is great and we go back to partying. If it doesn’t, we have a two step fallback.

  1. See if we have a single match for a similar icon. If we do, load that instead. For example, if I asked for lightbulb, it might suggest lightbulb-o.

  2. Provide a fallback generic icon. We are using a visually hidden square at the moment, but this could be anything. The best solution would be something contextually appropriate.

let iconSelector = type;
const validIcon = fontData.icons.filter((icon) => {
return icon.icon.tags.indexOf(type) > -1;
});
// If we don’t have an icon for this or there is more than one
// Throw a message at Raven
if (!validIcon.length) {
Raven.captureMessage(`The app has tried to load an invalid icon with type: ${type}`);
const suggested = fontData.icons.filter((icon) => {
return icon.properties.name.indexOf(type) > -1;
});
const suggestedNames = suggested.map((icon) => {
return icon.properties.name;
});
console.warn(`Warning: The app has tried to load an invalid icon with type: ${type}`);
console.info(`Suggested icons: ${suggestedNames.join(,)}`);
if (suggested.length === 1) {
iconSelector = suggested[0].icon.tags[0];
console.info(`Auto replaced icon with: ${iconSelector}`);
} else {
iconSelector = ‘square icon — hidden’;
console.info(‘Auto replaced and hidden icon with: icon-square’);
}
}

The same kind of checks can be used to change the markup for multicolored icon support.

Of course this is all well and good to be fixing the missing icons on the fly, but you also need to know the problem is happening…

Raven

When our icon request fails and we are providing fallback solutions, we fire off an error message to Raven with the icon request so we are aware of the issue and can put in a fix.

Raven.captureMessage(`The app has tried to load an invalid icon with type: ${type}`);

So next time you make an icon component, why not level it up?