Best Practices for SVG Icons in React
SVG icons are the modern standard for responsive, scalable UI elements. Learn how to convert, optimize, and integrate them into your React applications for optimal performance.
1. Why SVG Over Icon Fonts?
Icon fonts were the standard a decade ago, but SVG has clear advantages: smaller file sizes, better accessibility, easier to style with CSS, and no FOUT (flash of unstyled text) issues. Modern React projects should use SVG exclusively.
File Size Comparison
- Icon Font: 50-100 KB (loads entire font for few icons)
- Individual SVG: 1-5 KB per icon
- Optimized SVG as JSX: 0.5-2 KB (inline, no HTTP requests)
2. Optimization Techniques
Remove Unnecessary Attributes
Design tools export SVG with presentation attributes you don't need: width, height, fill colors, editor metadata. This converter removes them automatically.
Before:
<svg width="24" height="24" viewBox="0 0 24 24" fill="#333" xmlns="...">
After:
<svg viewBox="0 0 24 24" fill="currentColor">
Use currentColor for Theming
By setting fill to "currentColor", SVGs inherit text color from their parent. This enables single-source icons that work with light/dark modes and custom color schemes without duplicates.
3. React Component Best Practices
Prop-Based Configuration
Icons should accept props for size, color, and className. This pattern is consistent with Lucide, Heroicons, and other modern icon libraries.
export const CheckIcon: React.FC<IconProps> = ({
size = 24,
color = 'currentColor'
}) => (
<svg width={size} height={size} viewBox="0 0 24 24">
<path d="..." stroke={color} />
</svg>
);
Default to currentColor
This lets the icon inherit color from its parent element, matching text color automatically. Users can override with the color prop when needed.
4. Optimizing SVG Assets for Performance
Inline vs. External
- Inline (JSX): Best for design system icons. Zero HTTP requests. Inline bundle optimization.
- External (.svg): Use for large, complex illustrations. Cacheable. Better for multiple pages.
- Sprite Sheets: Legacy approach. Avoid unless supporting very old browsers.
Build-Time Optimization
Use SVGO (SVG Optimizer) in your build pipeline to compress all SVGs automatically. Most designs export with unnecessary metadata that SVGO strips safely.
Lazy Loading
For icon libraries with hundreds of icons, use dynamic imports and code-splitting. Load icon components on-demand rather than bundling everything upfront.
5. Accessibility Considerations
Decorative vs. Semantic Icons
- Decorative: Icon next to text. Add aria-hidden="true". Screen readers skip it.
- Semantic: Icon as button or standalone. Add role and aria-label props.
Decorative icon:
<CheckIcon aria-hidden="true" /> Success
Semantic icon button:
<button aria-label="Delete"><TrashIcon /></button>
6. Common Pitfalls to Avoid
- Forgetting viewBox: Always include viewBox. Width/height are optional but viewBox is mandatory for scaling.
- Hardcoded colors: Avoid fill="#FFF" or stroke="black". Use currentColor or pass as props.
- Missing aria-labels: Icon buttons are invisible to screen readers without proper labels.
- Overly complex SVG: Simplify paths. Use SVGO. Every extra node impacts performance.
- No fallbacks: Provide title tags for important icons. <title>Settings</title>
7. Design System Integration
Build a centralized icon component library with consistent sizing (16px, 24px, 32px), colors, and props. This ensures consistency across your application and makes icon updates trivial.
// icons/index.ts
export { CheckIcon } from './Check';
export { AlertIcon } from './Alert';
export { SearchIcon } from './Search';
// Usage
import { CheckIcon, AlertIcon } from '@/icons';
<CheckIcon size={24} color="#10b981" />
Pro Tip: Use TypeScript interfaces for icon props. Enable IntelliSense for developers using your icon library. Type-safe icons = fewer bugs.