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
svgsfolder. - 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! 😊