Throughout my experience developing websites with React, I frequently encountered challenges with icons (especially those not sourced from libraries). One key question was: How can I handle my icons more efficiently in code?
Why I Switched to SVGs
Initially, I used PNG and JPEG files for icons, but quickly realized their limitations. Switching to SVG files offered several advantages:
- Scalability: SVGs can scale without losing quality.
- Faster Loading: SVG files are lightweight, improving load times.
- CSS Styling: SVGs can be easily styled directly in CSS.
Creating Icon Components
After adopting SVGs, I structured my icon components like this:
export const ExampleIcon: React.FC = () => {
return (
<svg>
{ /* svg code here */ }
</svg>
);
};
While this worked, I wanted more flexibility in styling and leveraging React’s component power. To achieve that, I added props to my icon components:
export const ExampleIcon: React.FC<SVGProps<SVGSVGElement>> = (props) => {
return (
<svg {...props}>
{ /* svg code here */ }
</svg>
);
};
This allows us to pass all standard svg
element props to our icons.
Organizing Icons in React
To keep my icons organized, I created an icons
folder and added a new component whenever I needed a new icon. This method worked for a while, but I wondered—is there a more efficient approach?
Discovering @svgr/cli
After researching best practices, I came across @svgr/cli, a game-changing tool for handling SVGs in React. This CLI tool automatically generates React components from SVG files, saving time and improving maintainability.
###Generating Icon Components Automatically Instead of manually creating icon components, I could now run a single command to generate them:
npx @svgr/cli icons/svgs --out-dir icons/components --typescript
This command converts all SVG files in the svgs
folder into TypeScript React components, placing them in the icons/components
folder.
Building a Flexible Icon
Component
To streamline the use of icons, I built a reusable Icon
component. Here’s the folder structure I used:
src/
components/
icons/
svgs/
types/
components/icons/
: Contains generated icon components.svgs/
: Stores raw SVG files.types/
: Holds TypeScript types, including icon-type.ts.
Step 1: Define Icon Type
In src/types/icon-type.ts
, I defined the type for our icon components:
import type * as Icons from '../components/icons';
export type IconType = keyof typeof Icons;
This type ensures that IconType
corresponds to the names of the generated components, such as Logo
for a Logo.tsx
component.
Step 2: Create the Icon
Component
Next, I created the Icon.tsx
file in src/components/
:
import React, { type SVGProps } from 'react';
import type { IconType } from '../types/icon-type';
import * as Icons from './icons';
export type IconProps = SVGProps<SVGSVGElement> & {
icon: IconType;
};
export const Icon: React.FC<IconProps> = ({ icon, ...props }) => {
const Component = React.createElement(Icons[icon], props);
return (
<span className="custom-icon">
{Component}
</span>
);
};
This component dynamically renders the appropriate icon based on the icon
prop. For instance, to display the Logo
icon, you would use:
export const TestComponent: React.FC = () => {
return (
<div>
<Icon icon="Logo" />
</div>
);
};
Step 3: Styling the Icons
To ensure icons inherit the current text color, add the following to your global CSS file:
.custom-icon path, .custom-icon rect {
stroke: currentColor;
}
Automating Icon Generation
To simplify the process, I added the following script to my package.json
file:
"icons:generate": "npx @svgr/cli src/svgs --out-dir src/components/icons --typescript"
The Final Flow
Whenever you need to add a new icon to your project, simply:
- Place the SVG file in the
svgs
folder. - Run
npm run icons:generate
. - Use the new icon by referencing it in the
<Icon/>
component with autocomplete support for icon names.
I hope this article helps you handle icons more effectively in your React projects. Feel free to ask any questions — I’d love to help! 😊