React: Proper Icon Usage
Intro
There are many ways to use icons in React applications, however after trying out several methods in multiple applications I have landed on the following technique being my favorite. In this article we are going to be using icons from heroicons although the process would be the same for any icon that you can download in an SVG format.
Creating Icon Components
To start we want to turn each individual icon that we use into a functional component. Once we have a component for each icon we can simply reference that component instead of inlining the same SVG multiple times. The color of the icon is passed in as a prop, so that we can change the color of multiple icons at once without having to go into each component manually.
import React from "react"
const Hamburger = ({ Color }) => {
//highlight-line
//highlight-line
return (
<>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke={Color} //highlight-line
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M4 6h16M4 12h16M4 18h16"
/>
</svg>
</>
)
}
export default Hamburger
Also note that we have placed our icon inside the folder components/icons for organizational purposes. This is an example of one icon. Repeat this process for every icon that you use.
Using Icon Components
Now let's look at how these icons would be used in a menu. In this example we have a header component that uses a LandingMenu and LandingMenuItem component, which are both totally re-usable. Because we are passing the icons that are being used with these components as props, we can vastly cut down on the amount of repetitive code. This method and DRY and readable.
import React, { Component } from "react"
/* Components */
import LandingMenu from "./LandingMenu"
import LandingMenuItem from "./LandingMenuItem"
/* Icons */
import ChartBar from "./icons/ChartBar"
import Beaker from "./icons/Beaker"
import Support from "./icons/Support"
import Template from "./icons/Template"
import ChevronDown from "./icons/ChevronDown"
/* Styles */
import "../../styles/variables.css"
class LandingHeader extends Component {
/* Icon Colors */
color = "var(--icon-color)" //highlight-line
render() {
return (
<>
<LandingMenu Title="Features" Icon={<ChevronDown />}>
<LandingMenuItem
Icon={<ChartBar Color={this.color} />}
Title="Analysis"
Description="We provide an amazing service for your customers."
/>
<LandingMenuItem
Icon={<Beaker Color={this.color} />}
Title="Product Tracking"
Description="Our widgets are the best in the business."
/>
<LandingMenuItem
Icon={<Support Color={this.color} />}
Title="Support"
Description="There is only a 50% chance we will put you on hold for an hour and hang up on you."
/>
<LandingMenuItem
Icon={<Template Color={this.color} />}
Title="Item Title"
Description="This is a solid description of our widget."
/>
</LandingMenu>
</>
)
}
}
export default Header
Also note that we simply specified the color as a variable, which then references a CSS variable. In this way we are still specifying the color of the icons in our CSS files, which satisfies the separation of concerns principle. We can change the color of all the icons at once easily inside our CSS. We could also just as easily use multiple colors by using multiple variables here, such as icon-color-primary
and icon-color-secondary
etc etc.
Alternate Icon Sets
Because not all icon SVG's are formatted the same way that heroicons are, you sometimes have to tinker with the SVG code to get this system to work for you. Here are instructions for alternate icon sets.
To start with make sure that you download an SVG formatting extension for your text editor, that will make this process much easier.
IBM Icons
One of my personal favorite icon sets, and the set that is in use on this website. The IBM icon set is free, massive and very clean. However you can't just copy and paste the SVG inline like you would with some others.
One solution to this if you are making a React application is to use the IBM Icons NPM Package which is a fine solution. I don't like to include libraries if I don't have to, especially if i'm not confident it's going to get tree shaken. It is not a large amount of work to use the icons I need individually. Here is how the IBM icons are formatted directly off the website
<svg id="icon" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<defs>
<style>
.cls-1 {
fill: none;
}
</style>
</defs>
<title>3D-software</title>
<path
d="M21.49,13.1151l-9-5a1,1,0,0,0-1,0l-9,5A1.0078,1.0078,0,0,0,2,14v9.9951a1,1,0,0,0,.52.87l9,5A1.0045,1.0045,0,0,0,12,30a1.0559,1.0559,0,0,0,.49-.1349l9-5A.9923.9923,0,0,0,22,24V14A1.0079,1.0079,0,0,0,21.49,13.1151ZM11,27.2951l-7-3.89v-7.72l7,3.89Zm1-9.45L5.06,14,12,10.1351l6.94,3.86Zm8,5.56-7,3.89v-7.72l7-3.89Z" />
<polygon points="30 6 26 6 26 2 24 2 24 6 20 6 20 8 24 8 24 12 26 12 26 8 30 8 30 6" />
<rect
id="_Transparent_Rectangle_"
data-name="<Transparent Rectangle>"
class="cls-1"
width="32" height="32"
/>
</svg>
To clean this up we need to delete the whole defs
section. Up in the SVG section we add a fill
which is where we give it our color prop, and then down in rect
we delete the id
+ data-name
and change the class
to "ibm-icon" or whatever you prefer. So our final IBM icon component will look like this.
import React from "react"
const Dashboard = ({ Color }) => {
//highlight-line
return (
<>
<svg
id="icon"
xmlns="http://www.w3.org/2000/svg"
width="32"
height="32"
viewBox="0 0 32 32"
fill={Color} //highlight-line
>
<title>dashboard</title>
<rect x="24" y="21" width="2" height="5" />
<rect x="20" y="16" width="2" height="10" />
<path d="M11,26a5.0059,5.0059,0,0,1-5-5H8a3,3,0,1,0,3-3V16a5,5,0,0,1,0,10Z" />
<path d="M28,2H4A2.002,2.002,0,0,0,2,4V28a2.0023,2.0023,0,0,0,2,2H28a2.0027,2.0027,0,0,0,2-2V4A2.0023,2.0023,0,0,0,28,2Zm0,9H14V4H28ZM12,4v7H4V4ZM4,28V13H28.0007l.0013,15Z" />
<rect class="ibm-icon" width="32" height="32" /> //highlight-line
</svg>
</>
)
}
export default Dashboard
Lastly you need to make sure that you define the ibm-icon
CSS class with fill: none
or you will just get a black square where the logo should be.
.ibm-icon {
fill: none;
}
Comments
Recent Work
Basalt
basalt.softwareFree desktop AI Chat client, designed for developers and businesses. Unlocks advanced model settings only available in the API. Includes quality of life features like custom syntax highlighting.
BidBear
bidbear.ioBidbear is a report automation tool. It downloads Amazon Seller and Advertising reports, daily, to a private database. It then merges and formats the data into beautiful, on demand, exportable performance reports.