Base
Icon
Icon resolves a curated `name` to a built-in icon, or accepts any compatible SVG icon component directly via the `icon` prop.
Installation
npx shadcn add https://rck.vibeboxph.com//r/icon.jsonLive preview & controls
Controls
<Icon />Anatomy
IconThe rendered SVG element.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| name | IconName | — | Name of a curated icon from the built-in registry. |
| icon | IconComponent | — | Any compatible SVG icon component, takes precedence over `name`. |
| size | "xs" | "sm" | "md" | "lg" | "xl" | number | md | Pixel size. |
| title | string | — | Accessible label; omit for decorative icons (default aria-hidden). |
Accessibility
- Decorative by default (`aria-hidden="true"`) since icons are almost always paired with visible text.
- Pass `title` to render `role="img"` with `aria-label` for icons used without an adjacent text label.
Source
registry/rck/ui/icon.tsx
import * as React from "react";
import { cn } from "@/lib/utils";
import {
Bell,
Calendar,
Check,
ChevronDown,
ChevronRight,
Circle,
Copy,
ExternalLink,
Heart,
Info,
LoaderCircle,
Lock,
Mail,
Menu,
Moon,
Plus,
Search,
Settings,
Star,
Sun,
Trash2,
TriangleAlert,
User,
X,
type IconComponent,
} from "@/lib/icons";
/**
* Curated name -> icon map. Consumers can also pass any compatible SVG
* component directly via the `icon` prop.
*/
export const iconRegistry = {
bell: Bell,
calendar: Calendar,
check: Check,
"chevron-down": ChevronDown,
"chevron-right": ChevronRight,
circle: Circle,
copy: Copy,
"external-link": ExternalLink,
heart: Heart,
info: Info,
loader: LoaderCircle,
lock: Lock,
mail: Mail,
menu: Menu,
moon: Moon,
plus: Plus,
search: Search,
settings: Settings,
star: Star,
sun: Sun,
trash: Trash2,
warning: TriangleAlert,
user: User,
close: X,
} satisfies Record<string, IconComponent>;
export type IconName = keyof typeof iconRegistry;
const iconSizes = {
xs: 12,
sm: 16,
md: 20,
lg: 24,
xl: 32,
} as const;
export interface IconProps extends React.SVGAttributes<SVGSVGElement> {
/** Name of a curated icon from `iconRegistry`. */
name?: IconName;
/** Any compatible SVG component, takes precedence over `name`. */
icon?: IconComponent;
size?: keyof typeof iconSizes | number;
/** Accessible label. Omit for purely decorative icons (default: aria-hidden). */
title?: string;
}
export const Icon = React.forwardRef<SVGSVGElement, IconProps>(
({ name, icon: IconProp, size = "md", className, title, ...props }, ref) => {
const Component = IconProp ?? (name ? iconRegistry[name] : undefined) ?? Circle;
const numericSize = typeof size === "number" ? size : iconSizes[size];
return (
<Component
data-slot="icon"
size={numericSize}
className={cn("shrink-0", className)}
aria-hidden={title ? undefined : true}
role={title ? "img" : undefined}
aria-label={title}
{...props}
/>
);
},
);
Icon.displayName = "Icon";
Related examples
Sizes & colorThe size scale and color inheritance.
Icon gridIcons used inside a responsive action grid.