DEV Community

albert nahas
albert nahas

Posted on • Originally published at leandine.hashnode.dev

Adding Custom SVG Icons to Your React Application

SVG icons have become the standard for sharp, scalable graphics on the web, and React’s component-based model makes it a natural fit for integrating them. Whether you’re building a design system, a dashboard, or a marketing site, you’ll eventually need to add custom SVG icons to your React application. But what’s the best way to handle them? Should you inline SVGs, turn them into components, use sprites, or load them dynamically? Each approach comes with its own trade-offs regarding maintainability, performance, and developer experience.

Let’s walk through four popular strategies for using custom SVG icons in React: inline SVG, SVG as React components, SVG sprite sheets, and dynamic import. We’ll compare their pros and cons, provide TypeScript-based code examples for each, and help you choose the right method for your project.

1. Inline SVG: Direct Embedding in JSX

The most straightforward way to use an SVG icon is to copy its markup directly into your React component. This approach gives you full control over the SVG’s structure and styling.

// src/components/CheckIcon.tsx
import React from "react";

export const CheckIcon: React.FC<React.SVGProps<SVGSVGElement>> = (props) => (
  <svg
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    width={24}
    height={24}
    {...props}
  >
    <path d="M5 13l4 4L19 7" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round" />
  </svg>
);
Enter fullscreen mode Exit fullscreen mode

Usage:

<CheckIcon style={{ color: "green" }} />
Enter fullscreen mode Exit fullscreen mode

Pros

  • Maximum flexibility: Directly edit paths, add accessibility tags, animate parts, or change colors with props.
  • No build tooling required: Works in any React app out of the box.

Cons

  • Duplication risk: Copy-pasting SVGs can lead to repeated code if you use the same icon in multiple places.
  • Not scalable for large icon sets: Managing dozens of inline components quickly becomes tedious.

Best for

  • Small projects with a handful of unique icons.
  • One-off icons that need heavy customization.

2. SVG as React Component: Importing SVGs with a Loader

Modern React toolchains (Create React App, Vite, Next.js, etc.) can transform SVG files into reusable React components using build tools. This lets you keep your SVGs as files but work with them as components.

Example (with SVGR):

Suppose you have assets/icons/arrow-right.svg:

<!-- arrow-right.svg -->
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor">
  <path d="M5 12h14M13 6l6 6-6 6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
Enter fullscreen mode Exit fullscreen mode

Import as a component:

import { ReactComponent as ArrowRightIcon } from '../assets/icons/arrow-right.svg';

<ArrowRightIcon width={32} height={32} style={{ color: "blue" }} />
Enter fullscreen mode Exit fullscreen mode

Tip: If you’re using Vite, Next.js, or CRA, check their docs for SVG import support or install SVGR.

Pros

  • Component ergonomics: Use icons as regular React components, pass props, and compose easily.
  • Centralized icon files: Organize SVGs in a directory, enabling design/development handoff.
  • Supports tree-shaking: Unused icons are excluded from production builds.

Cons

  • Requires build configuration: Not all setups support this out of the box.
  • Large icon sets may bloat bundles: If you import all icons at once, you could increase your JS bundle size.

Best for

  • Medium-sized apps with a moderate number of icons.
  • Teams who want to keep SVG files separate from code.

3. SVG Sprites: The Classic Web Approach

SVG sprites bundle multiple icons into a single SVG file, each identified by a unique <symbol>. You reference icons using <use xlinkHref="#icon-id" /> inside your React components. This technique can save bandwidth and improve performance by making use of browser caching and reducing HTTP requests.

Step 1: Create a sprite (e.g., with svg-sprite-loader or svg-sprite).

<!-- sprite.svg -->
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
  <symbol id="icon-search" viewBox="0 0 24 24">
    <circle cx="11" cy="11" r="8" stroke="currentColor" stroke-width="2" fill="none"/>
    <line x1="21" y1="21" x2="16.65" y2="16.65" stroke="currentColor" stroke-width="2"/>
  </symbol>
  <!-- more icons -->
</svg>
Enter fullscreen mode Exit fullscreen mode

Step 2: Reference in React

type IconProps = {
  name: string;
  width?: number;
  height?: number;
  color?: string;
};

export const SpriteIcon: React.FC<IconProps> = ({
  name, width = 24, height = 24, color = "currentColor"
}) => (
  <svg width={width} height={height} fill="none" style={{ color }}>
    <use xlinkHref={`#icon-${name}`} />
  </svg>
);

// Usage
<SpriteIcon name="search" color="gray" />
Enter fullscreen mode Exit fullscreen mode

Note: You must ensure the sprite SVG is loaded into the DOM on page load, typically by including it at the root of your HTML or via a React portal.

Pros

  • Performance: One HTTP request for all icons, uses browser cache efficiently.
  • Consistent styling: All icons share the same color, size, etc., by default.

Cons

  • Setup complexity: Generating and injecting sprites requires extra tooling.
  • Limited dynamic coloring: Changing icon color or style can be less flexible than with component-based SVGs.
  • Not SSR-friendly: With server-side rendering, managing sprite injection can become tricky.

Best for

  • Large-scale applications with hundreds of icons.
  • Projects where performance and cache efficiency are high priorities.

4. Dynamic Import: Lazy Loading Icons on Demand

For large icon sets or when you want to optimize performance, dynamically import SVG icon components only when they’re needed. This prevents bundling all icons into your initial JavaScript payload.

import React, { Suspense, lazy } from "react";

type IconName = "home" | "settings" | "user";

const iconMap: Record<IconName, () => Promise<{ default: React.ComponentType<React.SVGProps<SVGSVGElement>> }>> = {
  home: () => import("../assets/icons/home.svg"), // SVGR or similar setup
  settings: () => import("../assets/icons/settings.svg"),
  user: () => import("../assets/icons/user.svg"),
};

type DynamicIconProps = {
  name: IconName;
  [key: string]: any;
};

export const DynamicIcon: React.FC<DynamicIconProps> = ({ name, ...props }) => {
  const IconComponent = lazy(iconMap[name]);
  return (
    <Suspense fallback={<span style={{ width: 24, height: 24, display: "inline-block" }} />}>
      <IconComponent {...props} />
    </Suspense>
  );
};

// Usage
<DynamicIcon name="user" width={28} color="purple" />
Enter fullscreen mode Exit fullscreen mode

Pros

  • Optimized bundles: Only load what you need, when you need it.
  • Scalable: Great for massive icon libraries or user-customizable icon sets.

Cons

  • Slightly more complex code: Requires React Suspense and dynamic import patterns.
  • May cause loading flicker: Use a placeholder to avoid UI jumps while the icon loads.

Best for

  • Applications with very large or rarely used icon sets.
  • User interfaces where icons are loaded on demand (e.g., plugins, themes).

Comparing with React Icon Libraries

You might wonder: why not just use a popular react icon library like react-icons, @heroicons/react, or @mui/icons-material? These libraries are fantastic for projects that need common icons, fast prototyping, or consistent design language. They offer:

  • Pre-built, accessible SVG icon components
  • Tree-shakable packages
  • Easy customization via props

However, when you require custom SVG icons—for branding, unique UI, or proprietary designs—you’ll need to manage your own icons using one of the strategies above.

If you’re looking to create or manage your own SVGs more efficiently, tools like SVGR, IcoMoon, Iconify, or AI-powered platforms like IcoGenie can help generate, convert, and organize SVG assets for use in React.

Key Takeaways

  • Inline SVG is simple and flexible, ideal for small projects or unique one-off icons.
  • SVG as React component offers a balance of maintainability and developer experience, fitting most modern React workflows.
  • SVG sprites are best when you want maximal caching and have a large, mostly static icon set, though they require more tooling.
  • Dynamic import helps optimize bundle size for very large or rarely used icon sets, at the cost of some complexity.

The right approach often depends on your team’s workflow, the size and dynamism of your icon set, and your build tooling. By understanding these techniques, you can keep your React SVG icons sharp, efficient, and a joy to use across your application.

Top comments (0)