TypeScript

The package is written in TypeScript and ships its own declarations. There is nothing to install from @types.

Exported types

Five, and that is all of them:

TypeScript
import type {
  IconName,
  IconStyle,
  IconVariant,
  IconProps,
  IconComponentType,
} from '@devigner-ui/icons';

IconProps

TypeScript
interface IconProps extends SVGProps<SVGSVGElement> {
  className?: string;
  variant?: IconVariant;
  strokeWidth?: string | number;
}

It extends React.SVGProps, so onClick, style, id, width, fill, data-* and the aria attributes are all accepted and all reach the <svg>.

IconStyle

TypeScript
type IconStyle = 'outline' | 'twotone' | 'bold' | 'bulk';

IconVariant

The same four drawings, spelled the way the variant prop takes them. The lowercase IconStyle is what the metadata, the CLI and the URLs use:

TypeScript
type IconVariant = 'Outline' | 'TwoTone' | 'Bold' | 'Bulk';

A style name maps to one variant:

TypeScript
const PROPS: Record<IconStyle, IconProps> = {
  outline: {},
  twotone: { variant: 'TwoTone' },
  bold: { variant: 'Bold' },
  bulk: { variant: 'Bulk' },
};

IconName

A literal union of all 2,146 icon names, without the Icon prefix that the components carry:

TypeScript
type IconName = '24Support' | '3dCubeScan' | 'ShoppingCart' | 'ArrowRight' | /* 2,141 more */;

const name: IconName = 'ShoppingCart';  // ✅
const wrong: IconName = 'NotAnIcon';    // ❌ Type error

The union is generated from the contents of icons/, so it cannot drift from what is exported.

IconComponentType

TypeScript
type IconComponentType = ForwardRefExoticComponent<
  IconProps & RefAttributes<SVGSVGElement>
>;

Not FC: every icon forwards its ref to the <svg>, which is what Radix, Headless UI and any tooltip or popover trigger need in order to measure and position against it.

Nothing more. The components carry no displayName, keywords, category or styles statics; that data lives in metadata.json.

Dynamic access by name

There is no IconMap export, and there cannot be one: an icon is called Map, so IconMap is already the name of that component. Use the namespace:

TSX
import * as Icons from '@devigner-ui/icons';
import type { IconName, IconComponentType } from '@devigner-ui/icons';

function renderIcon(name: IconName) {
  const Component = Icons[`Icon${name}`] as IconComponentType;
  return <Component className="size-6" />;
}

Note that this defeats tree shaking: the namespace import pulls in all 2,146 components. Prefer a hand-written map of the icons a screen actually uses:

TSX
import { IconShoppingCart, IconArrowRight } from '@devigner-ui/icons';
import type { IconComponentType } from '@devigner-ui/icons';

const ICONS = {
  cart: IconShoppingCart,
  next: IconArrowRight,
} satisfies Record<string, IconComponentType>;

Validating an unknown string

TSX
function SafeIcon({ name }: { name: string }) {
  const key = `Icon${name}`;
  if (!(key in Icons)) return null;
  const Component = Icons[key as keyof typeof Icons] as IconComponentType;
  return <Component className="size-6" />;
}

Typing the metadata

metadata.json is JSON, so it is a default import and needs resolveJsonModule. There is no exported IconMetadata type; declare the shape you rely on:

TypeScript
import metadata from '@devigner-ui/icons/metadata.json';
import type { IconStyle } from '@devigner-ui/icons';

interface IconMetadata {
  name: string;
  displayName: string;
  category: string;
  keywords: string[];
  pathCount: number;
  styles: Record<IconStyle, 'free' | 'pro'>;
  freeStyles: IconStyle[];
  proStyles: IconStyle[];
  usage: string;
}

const icons = metadata.icons as IconMetadata[];

Every icon is currently free in all four styles, so this reads the same for all of them, but read it rather than assume, since it is the one place that would change if anything is ever sold:

TypeScript
function freeStylesOf(name: string): IconStyle[] {
  return icons.find(i => i.name === name)?.freeStyles ?? [];
}

freeStylesOf('ShoppingCart');  // ['outline', 'twotone', 'bold', 'bulk']

Generic component

TSX
import * as Icons from '@devigner-ui/icons';
import type { IconName, IconStyle, IconComponentType, IconProps } from '@devigner-ui/icons';

const PROPS: Record<IconStyle, IconProps> = {
  outline: {},
  twotone: { variant: 'TwoTone' },
  bold: { variant: 'Bold' },
  bulk: { variant: 'Bulk' },
};

function TypedIcon({
  name,
  style = 'outline',
  className,
}: {
  name: IconName;
  style?: IconStyle;
  className?: string;
}) {
  const Component = Icons[`Icon${name}`] as IconComponentType;
  return <Component {...PROPS[style]} className={className} />;
}

<TypedIcon name="ShoppingCart" style="bold" />  // ✅
<TypedIcon name="InvalidIcon" />                // ❌ Type error

JSDoc for non-TypeScript projects

JavaScript
/**
 * @typedef {import('@devigner-ui/icons').IconProps} IconProps
 * @typedef {import('@devigner-ui/icons').IconStyle} IconStyle
 * @typedef {import('@devigner-ui/icons').IconName} IconName
 */

Documentation

Browse all 8,584 icons