Heroicons
Recipe
Instala @heroicons/react para usar el conjunto oficial de iconos de Tailwind Labs. Heroicons vienen en tres variantes: Outline (24px, trazo de 1.5px), Solid (24px, relleno) y Mini (20px, relleno).
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>
);
}Ejemplo Funcional
Una barra de navegación que usa iconos outline para estados por defecto e iconos solid para la ruta activa:
// 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>
);
}Profundización
Cómo Funciona
- Heroicons se organizan en tres rutas de importación que corresponden a las tres variantes:
@heroicons/react/24/outline-- iconos de 24x24 con trazos de 1.5px, ideales para navegación y barras de herramientas.@heroicons/react/24/solid-- iconos de 24x24 rellenos, excelentes para estados activos y énfasis.@heroicons/react/20/solid-- iconos de 20x20 rellenos (Mini), diseñados para uso inline con texto, botones y elementos de formulario.
- A diferencia de Lucide, Heroicons no tienen props
sizeocolor. Controlas dimensiones y color completamente a través declassName(por ejemplo,h-6 w-6 text-blue-500). - Esto hace que Heroicons sea la librería de iconos más nativa de Tailwind. Todas las propiedades visuales son clases de utilidad.
- El tree-shaking funciona por icono porque cada icono es una exportación nombrada desde su ruta de variante.
Variaciones
Cuándo usar cada variante:
| Variante | Ruta de Importación | Tamaño | Caso de Uso |
|---|---|---|---|
| Outline | 24/outline | 24x24 | Barras de navegación, barras de herramientas, configuración |
| Solid | 24/solid | 24x24 | Estados activos, acciones principales, énfasis |
| Mini | 20/solid | 20x20 | Inline con texto, dentro de botones, inputs de formulario |
Icono con botón de texto:
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>
);
}Icono animado:
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>
);
}Notas de TypeScript
- Todos los componentes de Heroicons están escritos como
React.ForwardRefExoticComponent<React.SVGProps<SVGSVGElement>>. - Aceptan todos los atributos SVG estándar más
className,styley atributos ARIA. - No hay un export de tipo dedicado
HeroIcon. UsaReact.ComponentType<React.SVGProps<SVGSVGElement>>para escribir icon props.
type HeroIcon = React.ComponentType<React.SVGProps<SVGSVGElement>>;
interface MenuItem {
icon: HeroIcon;
label: string;
}Gotchas
- Heroicons no aceptan props
sizeocolor. Establecersize={24}fallará silenciosamente. UsaclassName="h-6 w-6"en su lugar. - Las variantes outline y solid de 24px usan los mismos nombres de componentes (por ejemplo,
HomeIcon). Debes usar alias en uno cuando importes ambas variantes en el mismo archivo. - Los iconos Mini son 20x20, no 16x16. Si necesitas iconos de 16px, escala hacia abajo con
h-4 w-4pero ten en cuenta que el peso visual puede no estar optimizado para ese tamaño. - Heroicons v2 (la versión actual) tiene nombres de iconos diferentes a v1. Si estás migrando, consulta los cambios de nombres de iconos.
- Dado que Heroicons se basan en
currentColor, asegúrate de que el elemento padre tenga el color de texto correcto establecido.
Alternativas
| Enfoque | Ventajas | Desventajas |
|---|---|---|
| Heroicons | Integración perfecta con Tailwind, oficial de Tailwind Labs | Conjunto más pequeño (alrededor de 300 iconos) |
| Lucide React | Conjunto de iconos más grande, props size y color | Requiere configuración adicional de props |
| React Icons (Hi2) | Acceso a Heroicons junto con otras familias | Paquete más grande, dependencia indirecta |
| Phosphor Icons | Múltiples pesos (thin, light, regular, bold, fill, duotone) | No es del ecosistema de Tailwind |
FAQs
¿Cuáles son las tres variantes de Heroicons y sus rutas de importación?
- Outline:
@heroicons/react/24/outline-- 24x24, trazos de 1.5px, para navegación y barras de herramientas. - Solid:
@heroicons/react/24/solid-- 24x24, relleno, para estados activos y énfasis. - Mini:
@heroicons/react/20/solid-- 20x20, relleno, para uso inline con texto y botones.
¿Cómo controlas el tamaño y el color de un Heroicon?
- Heroicons no aceptan props
sizeocolor. - Usa clases de Tailwind:
className="h-6 w-6 text-blue-500". - Esto los hace la librería de iconos más nativa de Tailwind.
Gotcha: ¿Qué sucede si pasas size={24} a un componente Heroicon?
- Falla silenciosamente. La prop no es reconocida.
- Heroicons solo aceptan atributos SVG estándar y
className. - Usa
className="h-6 w-6"en su lugar.
¿Cómo usas ambas variantes outline y solid del mismo icono en un archivo?
import { HomeIcon as HomeOutline } from "@heroicons/react/24/outline";
import { HomeIcon as HomeSolid } from "@heroicons/react/24/solid";Debes usar alias en una importación ya que ambas variantes comparten el mismo nombre de componente.
¿Cuándo debes usar la variante Mini (20px) versus Outline o Solid?
- Usa iconos Mini inline con texto, dentro de botones y en elementos de formulario.
- Los iconos Mini son 20x20 y están optimizados para contextos de visualización más pequeños.
- Outline y Solid en 24x24 son mejores para barras de navegación y barras de herramientas.
¿Cómo alterna el ejemplo de NavBar entre iconos outline y solid?
- Compara el
pathnameactual (desdeusePathname()) con elhrefde cada enlace. - Si la ruta coincide, renderiza la variante Solid; de lo contrario, la variante Outline.
aria-current="page"se establece en el enlace activo para accesibilidad.
¿Cómo agregas una animación de giro a un Heroicon?
<ArrowPathIcon
className={`h-5 w-5 ${isLoading ? "animate-spin" : ""}`}
/>Dado que el estilo es completamente a través de className, las utilidades de animación de Tailwind funcionan directamente.
¿Qué tipo de TypeScript deberías usar para un Heroicon pasado como prop?
type HeroIcon = React.ComponentType<React.SVGProps<SVGSVGElement>>;
interface MenuItem {
icon: HeroIcon;
label: string;
}No hay un export de tipo dedicado HeroIcon del paquete.
Gotcha: ¿Son compatibles los nombres de iconos de Heroicons v1 y v2?
- No. Heroicons v2 tiene nombres de iconos diferentes a v1.
- Si estás migrando desde v1, consulta los cambios de nombres de iconos en las notas de lanzamiento de v2.0.0.
¿Por qué Heroicons se basan en currentColor y qué puede salir mal?
- Todos los Heroicons usan
currentColorpara su trazo o relleno. - Si el elemento padre no tiene un color de texto explícito, el icono hereda cualquier color establecido más arriba en el DOM.
- Siempre establece el color de texto correcto en el elemento padre o directamente en el icono con una clase de Tailwind.
¿Puedes escalar iconos Mini hacia abajo a 16x16 de forma segura?
- Puedes aplicar
h-4 w-4, pero el peso visual puede no estar optimizado para ese tamaño. - Los iconos Mini están diseñados para 20x20. Escalar por debajo de eso puede verse demasiado pesado o perder detalle.