Search across all documentation pages
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
Install @heroicons/react to use the official icon set from Tailwind Labs. Heroicons come in three variants: Outline (24px, 1.5px stroke), Solid (24px, filled), and Mini (20px, filled).
npm install @heroicons/react// app/components/heroicon-demo.tsx
"use client";
import { MagnifyingGlassIcon, Cog6ToothIcon } from "@heroicons/react/24/outline";
import { HomeIcon, BellIcon } from "@heroicons/react/24/solid";
import { ChevronDownIcon } from "@heroicons/react/20/solid";
export function HeroiconDemo() {
return (
<div className="flex items-center gap-4">
<MagnifyingGlassIcon className="h-6 w-6 text-gray-500" />
<Cog6ToothIcon className="h-6 w-6 text-gray-500" />
<HomeIcon className="h-6 w-6 text-blue-600" />
<BellIcon className="h-6 w-6 text-yellow-500" />
<ChevronDownIcon className="h-5 w-5 text-gray-400" />
</div>
);
}A navigation bar that uses outline icons for default states and solid icons for the active route:
// app/components/nav-bar.tsx
"use client";
import { usePathname } from "next/navigation";
import Link from "next/link";
import {
HomeIcon as HomeOutline,
MagnifyingGlassIcon as SearchOutline,
BellIcon as BellOutline,
UserIcon as UserOutline,
} from "@heroicons/react/24/outline";
import {
HomeIcon as HomeSolid,
MagnifyingGlassIcon as SearchSolid,
BellIcon as BellSolid,
UserIcon as UserSolid,
} from "@heroicons/react/24/solid";
interface NavLink {
href: string;
label: string;
outlineIcon: React.ElementType;
solidIcon: React.ElementType;
}
const navLinks: NavLink[] = [
{ href: "/", label: "Home", outlineIcon: HomeOutline, solidIcon: HomeSolid },
{ href: "/search", label: "Search", outlineIcon: SearchOutline, solidIcon: SearchSolid },
{ href: "/notifications", label: "Notifications", outlineIcon: BellOutline, solidIcon: BellSolid },
{ href: "/profile", label: "Profile", outlineIcon: UserOutline, solidIcon: UserSolid },
];
export function NavBar() {
const pathname = usePathname();
return (
<nav className="fixed bottom-0 left-0 right-0 border-t border-gray-200 bg-white">
<div className="mx-auto flex max-w-md items-center justify-around py-2">
{navLinks.map(({ href, label, outlineIcon: OutlineIcon, solidIcon: SolidIcon }) => {
const isActive = pathname === href;
const Icon = isActive ? SolidIcon : OutlineIcon;
return (
<Link
key={href}
href={href}
aria-label={label}
aria-current={isActive ? "page" : undefined}
className={`flex flex-col items-center gap-1 px-3 py-1 ${
isActive ? "text-blue-600" : "text-gray-500 hover:text-gray-700"
}`}
>
<Icon className="h-6 w-6" />
<span className="text-xs">{label}</span>
</Link>
);
})}
</div>
</nav>
);
}@heroicons/react/24/outline -- 24x24 icons with 1.5px strokes, ideal for navigation and toolbars.@heroicons/react/24/solid -- 24x24 filled icons, great for active states and emphasis.@heroicons/react/20/solid -- 20x20 filled icons (Mini), designed for inline use with text, buttons, and form elements.size or color props. You control dimensions and color entirely through className (e.g., h-6 w-6 text-blue-500).When to use each variant:
| Variant | Import Path | Size | Use Case |
|---|---|---|---|
| Outline | 24/outline | 24x24 | Navigation bars, toolbars, settings |
| Solid | 24/solid | 24x24 | Active states, primary actions, emphasis |
| Mini | 20/solid | 20x20 | Inline with text, inside buttons, form inputs |
Icon with text button:
import { PlusIcon } from "@heroicons/react/20/solid";
export function AddButton() {
return (
<button className="inline-flex items-center gap-1.5 rounded-md bg-blue-600 px-3 py-2 text-sm font-medium text-white hover:bg-blue-700">
<PlusIcon className="h-5 w-5" />
Add Item
</button>
);
}Animated icon:
import { ArrowPathIcon } from "@heroicons/react/24/outline";
export function RefreshButton({ isLoading }: { isLoading: boolean }) {
return (
<button aria-label="Refresh" className="p-2">
<ArrowPathIcon
className={`h-5 w-5 text-gray-600 ${isLoading ? "animate-spin" : ""}`}
/>
</button>
);
}React.ForwardRefExoticComponent<React.SVGProps<SVGSVGElement>>.className, style, and ARIA attributes.HeroIcon type export. Use React.ComponentType<React.SVGProps<SVGSVGElement>> for typing icon props.type HeroIcon = React.ComponentType<React.SVGProps<SVGSVGElement>>;
interface MenuItem {
icon: HeroIcon;
label: string;
}size or color props. Setting size={24} will silently fail. Use className="h-6 w-6" instead.HomeIcon). You must alias one when importing both variants in the same file.h-4 w-4 but be aware the visual weight may not be optimized for that size.currentColor, make sure the parent element has the correct text color set.| Approach | Pros | Cons |
|---|---|---|
| Heroicons | Perfect Tailwind integration, official Tailwind Labs | Smaller set (around 300 icons) |
| Lucide React | Larger icon set, size and color props | Requires additional prop configuration |
| React Icons (Hi2) | Access Heroicons alongside other families | Larger package, indirect dependency |
| Phosphor Icons | Multiple weights (thin, light, regular, bold, fill, duotone) | Not from Tailwind ecosystem |
@heroicons/react/24/outline -- 24x24, 1.5px strokes, for navigation and toolbars.@heroicons/react/24/solid -- 24x24, filled, for active states and emphasis.@heroicons/react/20/solid -- 20x20, filled, for inline use with text and buttons.size or color props.className="h-6 w-6 text-blue-500".className.className="h-6 w-6" instead.import { HomeIcon as HomeOutline } from "@heroicons/react/24/outline";
import { HomeIcon as HomeSolid } from "@heroicons/react/24/solid";You must alias one import since both variants share the same component name.
pathname (from usePathname()) to each link's href.aria-current="page" is set on the active link for accessibility.<ArrowPathIcon
className={`h-5 w-5 ${isLoading ? "animate-spin" : ""}`}
/>Since styling is entirely via className, Tailwind animation utilities work directly.
type HeroIcon = React.ComponentType<React.SVGProps<SVGSVGElement>>;
interface MenuItem {
icon: HeroIcon;
label: string;
}There is no dedicated HeroIcon type export from the package.
currentColor for their stroke or fill.h-4 w-4, but the visual weight may not be optimized for that size.Reviewed by Chris St. John·Last updated Jul 7, 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥