Fig. 29 A Guide for Developers

A React Icon Component That Works for Us

We figured out how to build a flexible icon system and component so you don't have to.

Written by Andy Rossi April 18, 2019

Icon systems are tricky creatures. They always become larger projects to maintain than they should. We spent a long time designing and developing a design system which included an extensive icon set. The pain points stemmed from trying to build a component that was easy to use by an implementing developer without increasing the complexity of the design system as a whole. We didn’t need a system that required a new component for every icon that was added.

What we needed was a component that could take any complex system that we threw at it.

And then, we got that complex system…

Designs were in flux, requirements were still being debated, and we still needed a drop-in component that could be used immediately.

The base requirements noted that all icons needed to be available at different pre-defined sizes. Some icons were simple UI elements: delete icons, confirmation icons, arrows, etc. Other icons were used as content and had a lot of visual detail (think small illustrations). Some icons were only available as small elements, other icons were only available at extra large sizes. And all icons and their variations needed to be documented. So yeah… things were all over the place.

Documentation would be supplied by Storybook in the form of an icon grid showing the icon and its name. Developers implementing the icons would then know what icon was available at which size.

Initially, we created a library of icons each with their own component: ArrowIcon, DeleteIcon, CheckIcon, CheckSmallIcon. This started to break down quickly since maintenance would be really REALLY annoying, so we went back to the drawing board.

“V2” started out promising. A standalone “Icon” component was written which took an icon name as a prop. This simplified the code and felt more natural, but things broke down as soon as we wanted to change sizes.

<Icon name="ArrowSmallIcon" /> was not going to scale. At this point, we had almost 30 available icons, and even with documentation, names were difficult to find and remember. This number would explode if we implemented all available sizes and variations. Nope. Not gonna do it. NEXT!

Then, a thought occurred.

Why not create icon objects with different path markup for each size? We could contain an icon series inside a single file, and add size keys to it when we need to add a new icon or icon size to the design system.

const arrow = {
  path: {
    small: '// jsx path markup',
    medium: '// jsx path markup',
    large: '// jsx path markup'
  }
}

With this setup, the Icon component could become more robust and help the implementer make the right choices about available icon sizes and configurations. So, we ended up with this:

Icon Component v3!

Note: check the side menu for folder structure, or visit the project directly on CodeSandbox.

It’s an icon component with brains! It even lets the developer know when it is used incorrectly without throwing an exception.

Benefits

  • Supplies name, size, and color props
  • Conditionally wraps the SVG in a containing element for more control over positioning and styling with the withContainer prop
  • Alerts the user if a specified size is not available, then defaults to the first available path (and lets them know this is happening via a console warning)
  • Is easy to configure if more features are needed
  • Gracefully fails if an unavailable icon is requested (with a console error listing out available icon names)
  • Easily styled with styled-components as well as allowing for custom class names for both the svg element and container element

Give it a try! Let us know what you think and if it can be improved. It works for us and it could work for you.