Let's paint the scene. You just started your career as a React web developer and you need to take an icon from a Figma file and put it next to the text in a link that gets the user into a funnel.
<a href="..." className="...">
Get Started
{/* Icon goes here */}
</a>
"What's the best way to do this?", you ask yourself.
You select the element in Figma, click "Export as SVG", and then paste it directly into the code. You feel pretty good about yourself as you go the extra mile to make the props more React friendly, like changing
class
to className
, stroke-width
to strokeWidth
, and so on.<a href="..." className="...">
Get Started
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="black"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="..."
>
<path d="..." />
</svg>
</a>
You start to move on to implementing other parts of the design file, but something feels like it's missing.
Improvement 1 - Make a component that you can reuse, reduce, and recycle
It finally hits you.
"What if I need to use this icon in other places in the code?"
You decide to move the icon to its own component. After all, React's core idea is building UIs out of reusable components.
You create a new folder called
icons
and add a new file called arrow-right.tsx
export function ArrowRight() {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="black"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="..."
>
<path d="..." />
</svg>
);
}
and then update the button to use that shiny new icon:
<a href="..." className="...">
Get Started
<ArrowRight />
</a>
"Ah, that feels better", you say to yourself as your eye twitches. Your body knows there's a better way.
Improvement 2 - Give me props
Something tugs at you as you try to move on to other parts of the design file. You let out a big sigh.
"What if I need to change some things about the icon when implementing it in other places?"
Then you remember learning about prop forwarding. You update your icon with some default values that can be updated in other implementations by simply passing a new value.
import type { SVGProps } from 'react';
type IconProps = SVGProps<SVGSVGElement>;
export function ArrowRight({
width = 24,
height = 24,
fill = 'none',
stroke = 'black',
strokeWidth = 2,
strokeLinecap = 'round',
strokeLinejoin = 'round',
className = '',
...props
}: IconProps) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width={width}
height={height}
fill={fill}
stroke={stroke}
strokeWidth={strokeWidth}
strokeLinecap={strokeLinecap}
strokeLinejoin={strokeLinejoin}
className={className}
{...props}
>
<path d="M6 9h6V5l7 7-7 7v-4H6V9z" />
</svg>
);
}
"Oh yeah, that's the good stuff," you say to yourself. "I didn't peak in high school after all."
Improvement 3 - Splash of currentColor
You finally move on to other parts of the design file and find that the icon was used in another place, just a little bigger and in a different color.
"I'm glad I did all that work to make a reusable component of the icon," you think to yourself.
As you start creating the element in the design files, you notice the icon is the same color as the text. You could just pass the HEX color of the text to the
stroke
property of the icon, but what if the icon could just inherit the color?After ~~clicking on the first link in a google search~~ some research, you learn about
currentColor
, which is a value you can pass to the stroke
and fill
properties of SVG elements. You are amazed when you learn that currentColor
will inherit the color
CSS property from its parent.Your
ArrowRight
icon has the stroke
defaulting to "black" and the fill
set to "none", so you only need to update the stroke
property to "currentColor".import type { SVGProps } from 'react';
type IconProps = SVGProps<SVGSVGElement>;
export function ArrowRight({
width = 24,
height = 24,
fill = 'none',
stroke = 'currentColor',
strokeWidth = 2,
strokeLinecap = 'round',
strokeLinejoin = 'round',
className = '',
...props
}: IconProps) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width={width}
height={height}
fill={fill}
stroke={stroke}
strokeWidth={strokeWidth}
strokeLinecap={strokeLinecap}
strokeLinejoin={strokeLinejoin}
className={className}
{...props}
>
<path d="..." />
</svg>
);
}
"This is it..." you say to yourself, "...this is the greatest component to ever exist."
You move on with the day, implementing your new icon where needed, absolutely crushing the design file with pixel-perfect accuracy, all while fighting the urge to create your own NPM package with this singular icon.
Conclusion
Sarcasm aside, this isn't too far off from my experience as a developer. I'm not declaring that this is the best way to handle SVG icons in your React app. In fact, I didn't exactly do this with the component library I built at work. At work, we use SVG sprites, based on a blog post by Ben Adam called The "best" way to manage icons in React.js. The main point of this post was to highlight some simple ways to improve your icons that I've discovered in my time as a web developer.
Thanks for making it to the end, enjoy this cookie 🍪.