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! 😊
Top comments (0)