React SME Cookbook
All FAQs

Search Documentation

Search across all documentation pages

heroiconsiconstailwindoutlinesolid

Heroicons

Recipe

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>
  );
}

Working Example

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>
  );
}

Deep Dive

How It Works

  • Heroicons are organized into three import paths that correspond to the three variants:
    • @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.
  • Unlike Lucide, Heroicons do not have size or color props. You control dimensions and color entirely through className (e.g., h-6 w-6 text-blue-500).
  • This makes Heroicons the most Tailwind-native icon library. Every visual property is a utility class.
  • Tree-shaking works per-icon because each icon is a named export from its variant path.

Variations

When to use each variant:

VariantImport PathSizeUse Case
Outline24/outline24x24Navigation bars, toolbars, settings
Solid24/solid24x24Active states, primary actions, emphasis
Mini20/solid20x20Inline 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>
  );
}

TypeScript Notes

  • All Heroicon components are typed as React.ForwardRefExoticComponent<React.SVGProps<SVGSVGElement>>.
  • They accept all standard SVG attributes plus className, style, and ARIA attributes.
  • There is no dedicated 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;
}

Gotchas

  • Heroicons do not accept size or color props. Setting size={24} will silently fail. Use className="h-6 w-6" instead.
  • The 24px outline and solid variants use the same component names (e.g., HomeIcon). You must alias one when importing both variants in the same file.
  • Mini icons are 20x20, not 16x16. If you need 16px icons, scale down with h-4 w-4 but be aware the visual weight may not be optimized for that size.
  • Heroicons v2 (the current version) has different icon names than v1. If migrating, check the icon name changes.
  • Since Heroicons rely on currentColor, make sure the parent element has the correct text color set.

Alternatives

ApproachProsCons
HeroiconsPerfect Tailwind integration, official Tailwind LabsSmaller set (around 300 icons)
Lucide ReactLarger icon set, size and color propsRequires additional prop configuration
React Icons (Hi2)Access Heroicons alongside other familiesLarger package, indirect dependency
Phosphor IconsMultiple weights (thin, light, regular, bold, fill, duotone)Not from Tailwind ecosystem

FAQs

What are the three Heroicons variants and their import paths?
  • Outline: @heroicons/react/24/outline -- 24x24, 1.5px strokes, for navigation and toolbars.
  • Solid: @heroicons/react/24/solid -- 24x24, filled, for active states and emphasis.
  • Mini: @heroicons/react/20/solid -- 20x20, filled, for inline use with text and buttons.
How do you control the size and color of a Heroicon?
  • Heroicons do not accept size or color props.
  • Use Tailwind classes: className="h-6 w-6 text-blue-500".
  • This makes them the most Tailwind-native icon library.
Gotcha: What happens if you pass size={24} to a Heroicon component?
  • It silently fails. The prop is not recognized.
  • Heroicons only accept standard SVG attributes and className.
  • Use className="h-6 w-6" instead.
How do you use both outline and solid variants of the same icon in one file?
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.

When should you use the Mini (20px) variant versus the Outline or Solid?
  • Use Mini icons inline with text, inside buttons, and in form elements.
  • Mini icons are 20x20 and optimized for smaller display contexts.
  • Outline and Solid at 24x24 are better for navigation bars and toolbars.
How does the NavBar example toggle between outline and solid icons?
  • It compares the current pathname (from usePathname()) to each link's href.
  • If the route matches, it renders the Solid variant; otherwise the Outline variant.
  • aria-current="page" is set on the active link for accessibility.
How do you add a spin animation to a Heroicon?
<ArrowPathIcon
  className={`h-5 w-5 ${isLoading ? "animate-spin" : ""}`}
/>

Since styling is entirely via className, Tailwind animation utilities work directly.

What TypeScript type should you use for a Heroicon passed as a prop?
type HeroIcon = React.ComponentType<React.SVGProps<SVGSVGElement>>;
 
interface MenuItem {
  icon: HeroIcon;
  label: string;
}

There is no dedicated HeroIcon type export from the package.

Gotcha: Are Heroicons v1 and v2 icon names compatible?
  • No. Heroicons v2 has different icon names than v1.
  • If migrating from v1, check the icon name changes in the v2.0.0 release notes.
Why do Heroicons rely on currentColor, and what can go wrong?
  • All Heroicons use currentColor for their stroke or fill.
  • If the parent element has no explicit text color, the icon inherits whatever color is set higher in the DOM.
  • Always set the correct text color on the parent or directly on the icon with a Tailwind class.
Can you scale Mini icons down to 16x16 safely?
  • You can apply h-4 w-4, but the visual weight may not be optimized for that size.
  • Mini icons are designed for 20x20. Scaling below that may look too heavy or lose detail.