React applications handle vector graphics through several distinct approaches. An SVG can load as an external static image, render directly as inline document markup, or compile into a functional React component through build tools like Vite and Webpack. Choosing among these approaches affects how your browser caches assets, how much JavaScript your users download, and whether you can style individual vector paths with CSS.

Loading SVGs through standard image tags
The simplest way to display a vector graphic in React is the standard HTML image element. You import the asset path or reference a URL from your public folder, then assign it to the image source attribute:
import logoUrl from "./assets/logo.svg";
function Header() {
return (
<header>
<img src={logoUrl} alt="Application Logo" width={160} height={40} />
</header>
);
}
This technique provides two primary benefits. First, the browser treats the SVG as an external image file, downloading and caching it independently from your JavaScript bundles. Second, the graphic adds zero bytes to your compiled script files, which keeps initial application load times fast.
The limitation of image tags is lack of internal control. Browsers isolate external images inside their own document context. You cannot change path colors with CSS variables, override fill values with utility classes like Tailwind, or trigger stateful animations on individual shapes inside the file. Standard image elements work best for fixed brand logos, static decorative illustrations, and imagery that never needs dynamic recoloring.
Transforming SVGs into React components with SVGR

When an icon must respond to user interaction, active navigation states, or dark mode themes, transforming the vector file into a React component is the most common industry solution. The SVGR library parses raw SVG markup and converts it into a functional component that accepts standard JSX props.
In Vite projects, you install the vite-plugin-svgr package and register it in your configuration file:
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import svgr from "vite-plugin-svgr";
export default defineConfig({
plugins: [react(), svgr()],
});
With the plugin active, you import the SVG file directly as a React component:
import StarIcon from "./assets/star.svg?react";
function RatingButton({ isFavorite, onToggle }) {
return (
<button onClick={onToggle} type="button">
<StarIcon
className={isFavorite ? "text-amber-500" : "text-gray-400"}
width={24}
height={24}
aria-hidden="true"
/>
<span>Favorite</span>
</button>
);
}
In Next.js or custom Webpack setups, SVGR functions through Webpack loaders. The resulting component injects the vector directly into the DOM tree. This architecture gives you complete CSS control over every stroke and fill.
Remember that React requires camelCase naming for SVG attributes. While SVGR converts standard attributes automatically during compilation, any manual inline editing must replace stroke-width with strokeWidth, fill-rule with fillRule, and clip-path with clipPath.
Direct inline markup
You can also paste raw SVG code directly into a component return statement:
function CloseIcon({ size = 20, className = "" }) {
return (
<svg
width={size}
height={size}
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={2}
strokeLinecap="round"
strokeLinejoin="round"
className={className}
aria-hidden="true"
>
<line x1="18" y1="6" x2="6" y2="18" />
<line x1="6" y1="6" x2="18" y2="18" />
</svg>
);
}
Direct markup is useful for small, specialized icons that exist in only one place in your codebase. It avoids configuration changes in your build pipeline. However, pasting extensive SVG paths directly into component files clutters source code and makes maintenance difficult over time.
Building a flexible icon system

Large frontend projects often accumulate dozens or hundreds of icons. Importing each icon individually across multiple screens creates scattered dependencies and inconsistent sizing. A central icon component keeps your interface uniform:
import { Bell, Search, Settings, User } from "./icons";
const iconMap = {
bell: Bell,
search: Search,
settings: Settings,
user: User,
};
export function Icon({ name, size = 20, className = "", label }) {
const Component = iconMap[name];
if (!Component) return null;
return (
<Component
width={size}
height={size}
className={className}
aria-hidden={!label}
role={label ? "img" : undefined}
aria-label={label}
/>
);
}
Setting vector fill or stroke properties to currentColor inside your source graphics allows the icon to inherit whatever text color its parent element uses. When placed inside a primary button, the icon automatically matches the button text color without manual overrides.
Bundle size management and sprite sheets

While importing SVGs as React components offers great flexibility, doing so for hundreds of icons creates an invisible performance penalty. Every imported SVG becomes a JavaScript function inside your bundle. A large icon set can easily add hundreds of kilobytes of JavaScript that the browser must download, parse, and execute during startup.
When an application requires an extensive icon library, SVG sprite sheets provide an efficient middle ground. A sprite sheet combines multiple vector graphics into a single file composed of symbol elements, each with a unique identifier:
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<symbol id="icon-check" viewBox="0 0 24 24">
<path d="M5 13l4 4L19 7" fill="none" stroke="currentColor" strokeWidth="2" />
</symbol>
</svg>
In your React components, you reference the desired graphic using the SVG use element:
function SpriteIcon({ id, size = 24, className = "" }) {
return (
<svg width={size} height={size} className={className} aria-hidden="true">
<use href={`/sprites.svg#${id}`} />
</svg>
);
}
Because the sprite file resides in your static public directory, the browser caches it like an image, while your components retain the ability to resize icons and set colors through CSS currentColor.
Essential practices for vector graphics in React
Before committing SVG files to your project repository, optimize them through tools like SVGO or web converters like SVG to Any. Optimization strips hidden editor metadata from software like Illustrator or Figma, removes unnecessary namespaces, and rounds coordinates to lower file size.
Always check that your exported SVGs retain a viewBox attribute. If a design tool exports a graphic with fixed width and height but omits the viewBox, the graphic will crop instead of scaling proportionally when you change its dimensions in React.