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.json

Live preview & controls

Controls
<Icon />

Anatomy

  • IconThe rendered SVG element.

Props

PropTypeDefaultDescription
nameIconNameName of a curated icon from the built-in registry.
iconIconComponentAny compatible SVG icon component, takes precedence over `name`.
size"xs" | "sm" | "md" | "lg" | "xl" | numbermdPixel size.
titlestringAccessible 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

Browse the full examples gallery