import { IconShoppingCart } from '@devigner-ui/icons';
function Example() {
return (
<div>
{/* Defaults: outline, sized by CSS */}
<IconShoppingCart className="size-6" />
{/* The four styles */}
<IconShoppingCart className="size-6" /> {/* outline */}
<IconShoppingCart variant="TwoTone" className="size-6" /> {/* twotone */}
<IconShoppingCart variant="Bold" className="size-6" /> {/* bold */}
<IconShoppingCart variant="Bulk" className="size-6" /> {/* bulk */}
{/* Size and colour come from the class */}
<IconShoppingCart className="size-8" />
<IconShoppingCart className="size-6 text-indigo-500" />
{/* Stroke width, in the 24px drawing's own units */}
<IconShoppingCart strokeWidth={1} className="size-6" />
<IconShoppingCart strokeWidth={3} className="size-6" />
</div>
);
}There are three props of its own (className, variant, strokeWidth) on
top of everything an <svg> takes. There is no size and no color: size and
colour are CSS, and strokes are currentColor, so text-* colours them.
A style is one variant string:
| Style | variant |
Fill | Stroke |
|---|---|---|---|
outline |
(the default) | ❌ | ✅ Full linework |
twotone |
"TwoTone" |
❌ | ✅ Secondary at half tone |
bold |
"Bold" |
✅ | ❌ One filled form |
bulk |
"Bulk" |
✅ | ❌ Secondary mass at half tone |
Defaults are variant = "Outline" and strokeWidth = "1.5", so a bare
<IconShoppingCart /> is outline at 1.5. strokeWidth only reaches linework,
which Bold and Bulk do not have.
All 8,584 icons are free and MIT. See the licensing guide.
To have the CLI write the right prop for you:
npx @devigner-ui/icons copy ShoppingCart --style=boldAn icon renders aria-hidden="true" and focusable="false" by default, so an
icon beside its own text label is not announced twice. Label it when it carries
meaning and the icon becomes role="img" instead:
<IconShoppingCart className="size-6" /> {/* decorative */}
<IconShoppingCart aria-label="Shopping cart" className="size-6" /> {/* role="img" */}Everything an <svg> accepts reaches the element, aria-* included, so there
is nothing to wrap. Inside a button that already has a label, leave the icon
decorative.
import metadata from '@devigner-ui/icons/metadata.json';
const cart = metadata.icons.find(i => i.name === 'ShoppingCart');
cart.freeStyles; // ['outline', 'twotone', 'bold', 'bulk']
cart.proStyles; // []metadata.json is JSON, so it is a default import; there is no named
metadata export.
proStyles is empty for every icon. There is nothing to unlock.
// app/layout.tsx
import { IconShoppingCart } from '@devigner-ui/icons';
export default function Layout({ children }) {
return (
<header>
<IconShoppingCart className="size-7" />
</header>
);
}The bundle carries a "use client" banner, so the icons are client components
and can be rendered from a server component without marking your own file.
import { IconShoppingCart } from '@devigner-ui/icons';Works as-is.
There is no IconMap export: one of the icons is called Map, so IconMap
is that component. For a small, known set, write the map out; it is the version
that tree shakes:
import { IconShoppingCart, IconArrowRight, IconUser } from '@devigner-ui/icons';
import type { IconComponentType } from '@devigner-ui/icons';
const ICONS = {
ShoppingCart: IconShoppingCart,
ArrowRight: IconArrowRight,
User: IconUser,
} satisfies Record<string, IconComponentType>;
function DynamicIcon({ name }: { name: keyof typeof ICONS }) {
const Component = ICONS[name];
return <Component variant="TwoTone" className="size-6" />;
}If you genuinely need all 8,584 icons reachable by string, use the namespace and accept that nothing tree shakes:
import * as Icons from '@devigner-ui/icons';
import type { IconComponentType } from '@devigner-ui/icons';
const Component = Icons[`Icon${name}`] as IconComponentType;import type { IconProps } from '@devigner-ui/icons';
interface IconProps extends SVGProps<SVGSVGElement> {
className?: string;
variant?: 'Outline' | 'TwoTone' | 'Bold' | 'Bulk';
strokeWidth?: string | number;
}IconName, IconStyle, IconVariant and IconComponentType are exported too; see the
TypeScript API.