The package is written in TypeScript and ships its own declarations. There is
nothing to install from @types.
Five, and that is all of them:
import type {
IconName,
IconStyle,
IconVariant,
IconProps,
IconComponentType,
} from '@devigner-ui/icons';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>.
type IconStyle = 'outline' | 'twotone' | 'bold' | 'bulk';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:
type IconVariant = 'Outline' | 'TwoTone' | 'Bold' | 'Bulk';A style name maps to one variant:
const PROPS: Record<IconStyle, IconProps> = {
outline: {},
twotone: { variant: 'TwoTone' },
bold: { variant: 'Bold' },
bulk: { variant: 'Bulk' },
};A literal union of all 2,146 icon names, without the Icon prefix that the
components carry:
type IconName = '24Support' | '3dCubeScan' | 'ShoppingCart' | 'ArrowRight' | /* 2,141 more */;
const name: IconName = 'ShoppingCart'; // ✅
const wrong: IconName = 'NotAnIcon'; // ❌ Type errorThe union is generated from the contents of icons/, so it cannot drift from
what is exported.
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.
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:
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:
import { IconShoppingCart, IconArrowRight } from '@devigner-ui/icons';
import type { IconComponentType } from '@devigner-ui/icons';
const ICONS = {
cart: IconShoppingCart,
next: IconArrowRight,
} satisfies Record<string, IconComponentType>;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" />;
}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:
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:
function freeStylesOf(name: string): IconStyle[] {
return icons.find(i => i.name === name)?.freeStyles ?? [];
}
freeStylesOf('ShoppingCart'); // ['outline', 'twotone', 'bold', 'bulk']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/**
* @typedef {import('@devigner-ui/icons').IconProps} IconProps
* @typedef {import('@devigner-ui/icons').IconStyle} IconStyle
* @typedef {import('@devigner-ui/icons').IconName} IconName
*/