React

How to create a reusable Icon component for your React application?

How to create a reusable Icon component

Today, I'll tell you about the approach we utilize in the Muvon team to work with SVG icons in our Design System. I adopted this method from my employer and found it exceedingly convenient and efficient.

Initial requirements

You probably already have some knowledge of React if you're reading this article. If you'd like to try it out on a new project, go ahead and use this command:

npx create-react-app my-app --template typescript

I personally prefer to use TypeScript, so the script above will create a new React application with TypeScript.

What props should we have?

Before we start writing code, let's think about what we expect from this component.

What properties should it support?

Since we are working with an icon, we will need color and size as a minimum. And since we are using TypeScript, let's describe the possible sizes of the icon using an enum. You can create a separate file for enums and types, or store it within your Icon.tsx file:

export enum IconSize {
  xs = '16px',
  s = '18px',
  m = '20px',
  l = '22px',
  xl = '24px',
  '2xl' = '26px',
}

I prefer to use T-shirt notation for different sizes. Well, looks good. Let’s go next.

Creating the <Icon /> component

Under Icon.tsx let’s create our base component:

export function Icon({ color = 'currentColor', size = IconSize.m}: IconProps) {
  return (
    <svg role="img" width={size} height={size} fill={color}>
      <use href="" />
    </svg>
   )
}

The element is a new feature for some people. It's a super cool and easy trick! All the convenience of our approach is built around this functionality.

To know more about the “use“: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/use

Creating the SVG Sprite

For our Icon component to work, we need to have a sprite containing all the icons we need. You can either create it manually or use a tool like SVGR to generate the sprite. Once you have the sprite, you can store it in your assets folder and import it.

Let's imagine that we want to start our design system with just 2 icons: Alert and Hamburger. This is how the SVG file should be created:

<svg xmlns="http://www.w3.org/2000/svg">
  <symbol viewBox="0 0 24 24" id="AlertFill" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M0 12C0 5.376 5.364 0 11.988 0 18.624 0 24 5.376 24 12s-5.376 12-12.012 12C5.364 24 0 18.624 0 12zm11-6a1 1 0 112 0v7a1 1 0 11-2 0V6zm0 11a1 1 0 112 0 1 1 0 01-2 0z"/></symbol>

  <symbol viewBox="0 0 24 24" id="Hamburger" xmlns="http://www.w3.org/2000/svg"><path d="M5 10c-.6 0-1-.4-1-1s.4-1 1-1h14c.6 0 1 .4 1 1s-.4 1-1 1H5zm0 6c-.6 0-1-.4-1-1s.4-1 1-1h14c.6 0 1 .4 1 1s-.4 1-1 1H5z"/></symbol>
</svg>

Every symbol should have a unique ID attribute, as this is how we determine which icon to display in our component.

To store the SVG sprite, we need to create a separate Container component:

import { ReactComponent as SpriteSvg } from './sprite.svg'

export function IconsSpriteContainer() {
  return (
    <div style={{ display: 'none' }}>
      <SpriteSvg />
    </div>
  )
}

It's important to hide it using the display: none attribute, as the sprite will contain all icons from the website, which could be a large number and showing them could brake the UI.

Remember to place the component somewhere, as it should be available on every page.

Continuing with the Icon component

Cool, now we have 2 icons! Hooray!

As we're using TypeScript, let's extract the icon IDs (names) into an enum:

export enum IconNames {
  hamburger = 'Hamburger'
  alertFill = 'AlertFill'
}

Now we can describe the IconProps, that we used earlier:

export type IconProps = {
  name?: IconNames
  color?: string
  size?: IconSize
}

Final code

Note that our SVGs have IDs, so we pick them using the # symbol. This is important.

export function Icon({
  name = IconNames.alertFill,
  color = 'currentColor',
  size = IconSize.m,
}: IconProps) {
  return (
    <svg role="img" width={size} height={size} fill={color}>
      <use href={`#${name}`} />
    </svg>
  )
}

Looks like we're done here! Let's use our component somewhere and test it:

<div>
  <Icon name={IconNames.alertFill} />
  <Icon color="var(--color-primary)" name={IconNames.hamburger} />
</div>

Note that you can use not only different HEX and RGBA values to pick the color, but also CSS variables. Cool, right? (The default icon color is white, by the way.)


*Not every SVG is easy to convert into a , so I’m using https://svgsprit.es/

Thanks for reading Muvon’s Newsletter! Subscribe for free to receive new posts and support our work.

A blog for self-taught engineers

Сommunity is filled with like-minded individuals who are passionate about learning and growing as engineers.