There are exactly three sensible ways to get an SVG icon into a React codebase: paste it inline as a component, import the file through a build transform like SVGR, or reference it from a sprite. Most projects need only the first. This guide walks through the inline approach with Next.js and Tailwind specifics, and points to the deeper guides where a topic deserves its own article.
Option 1: an inline JSX component
Take a real icon from the catalog, convert the SVG attributes to JSX casing, and you have a dependency-free component. This is Lucide's search icon, exactly as it ships in the Lucide set, wrapped for React:
export function SearchIcon(props) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
aria-hidden="true"
{...props}
>
<path d="m21 21l-4.34-4.34" />
<circle cx="11" cy="11" r="8" />
</svg>
);
}
The JSX gotchas are all attribute casing: stroke-width becomes strokeWidth, stroke-linecap becomes strokeLinecap, and class becomes className. Icon pages on this site do the conversion for you: every icon offers React, Vue, Svelte, and Solid snippets next to the raw SVG, so you can copy the JSX form directly. If you have a folder of SVG files instead, the free SVG to component converter batch-converts them in the browser.
Prefer importing .svg files over pasting? That is the SVGR route, covered step by step in our React with Vite and SVGR guide.
Next.js: server components by default
An icon component like the one above has no state, no effects, and no event handlers, which makes it a perfect React Server Component. In the Next.js App Router it renders to static markup on the server and adds nothing to the client bundle:
import { SearchIcon } from "@/components/icons";
export default function DocsHeader() {
return (
<label className="flex items-center gap-2">
<SearchIcon className="h-5 w-5 text-zinc-400" />
<input type="search" placeholder="Search docs" />
</label>
);
}
Only add "use client" when the icon itself participates in interactivity, for example an animated toggle. A common Next.js mistake is marking a whole layout as client code just because one icon has a hover effect that CSS could have handled.
Tailwind: size with utilities, color with currentColor
Because well-drawn icon sets inherit currentColor, Tailwind's text color utilities style icons with no extra work. Size with h-* and w-*, color with text-*, and state variants compose naturally:
<button className="group flex items-center gap-2 text-zinc-500 hover:text-zinc-900">
<SearchIcon className="h-4 w-4" />
<span>Search</span>
</button>
The icon reads the button's text color, so hover, focus, dark mode, and disabled states all come from one utility change on the parent. The mechanics of currentColor theming, including CSS variables for multi-tone icons, are covered in our themeable icon components guide. Stroke-drawn sets in the UI 24px category, like Lucide and Tabler, are drawn for exactly this workflow.
Accessibility: decorative by default
Most icons sit next to a visible label, and those should be hidden from assistive tech with aria-hidden="true", as the component above does. An icon that carries meaning on its own, like an icon-only button, needs an accessible name on the interactive element instead:
<button aria-label="Search" className="p-2">
<SearchIcon className="h-5 w-5" />
</button>
The full decision tree, including when role="img" and <title> are the right tool, lives in our SVG icon accessibility guide.
When a sprite earns its keep
Inline components are the right default, but a page that repeats the same icons hundreds of times, a data table with row actions for instance, can trim HTML weight with a sprite: define each shape once, reference it with <use>. The free SVG sprite generator builds the symbol sheet from pasted icons, and directional glyphs from the arrow icons hub are the classic candidates, since tables tend to repeat them most.
Getting the icons themselves
Any icon in the catalog can be copied as clean SVG or as a framework snippet from its page. When a project settles on its icon language, signed-in users can collect the chosen icons into an Icon Collection and export them together, which keeps a team from re-picking icons one pull request at a time. For choosing the set in the first place, our Lucide vs Tabler vs Phosphor comparison covers the three most popular starting points.
This article was originally published on SVGicons.com.
Top comments (0)