Head-to-head comparison of Lucide, Heroicons, Material Symbols, and Radix Icons for shadcn/ui projects — style, coverage, bundle size, and a clear verdict.
There are two similarly named things in this ecosystem:
ui.shadcn.com: The official code project and CLI. When you run npx shadcn-ui@latest add button, it copies the React component source into your repository (no npm dependency). These components are pre-wired to Lucide by default via your components.json ("iconLibrary": "lucide").
shadcndesign.com: Our third-party, commercial Figma kit that mirrors the official shadcn/ui components for design work. It assumes Lucide in Figma for visual parity with the default code. Its "add custom icons" docs are about Figma workflows (publishing libraries, building components), not about changing your React imports or the shadcn/ui CLI.
Keep that split in mind: design-time (Figma) vs. runtime (React SVG components). Choosing an icon library affects code, while shadcndesign helps designers stay in sync with what developers see by default (Lucide). If you switch icon sets in code, coordinate your Figma assets (e.g., replace Lucide glyphs in the Figma library you use, whether it's shadcndesign or your own).
shadcn/ui follows an "open code" model: the CLI copies component files into your repo. Those files import icons from lucide-react by default. The default is set in components.json (iconLibrary: "lucide"). If you keep Lucide, everything just works; both in code and, for design teams using shadcndesign, in Figma.
If you change icon libraries, you're changing imports inside your copied components (and you must keep doing that each time you add new shadcn/ui components). That's the primary friction point to plan for.
Helps: Gives designers a high-fidelity Figma kit aligned with shadcn/ui defaults (including Lucide look), so designs match code out of the box.
Doesn't: Change your React imports or the shadcn/ui CLI. If engineering adopts, say, HeroIcons in code, the design team should also update the Figma icon source (swap Lucide frames for HeroIcons or publish an internal icon library) to prevent drift. shadcndesign can still be your base kit; you'll just replace the icon assets it ships with.
Style & scope: Clean, minimal, outline-only set (24×24, default 2px stroke). Large library (~1.6k+ icons).
DX: Import individual React components (tree-shakable). Props like size, color, and strokeWidth make it easy to tune weight and fit.
A11y: Guidance provided; developers control aria-hidden, labels, and context.
License: ISC (permissive).
strokeWidth prop is great for matching visual weight.Style & scope: Designed by Tailwind Labs. Offers Outline, Solid, plus Mini/Micro sizes. Smaller, curated set (~300 unique glyphs).
DX: Install @heroicons/react or add per-icon SVGs using the community shadcn registry.
A11y: Sensible default of aria-hidden="true" for decorative icons.
License: MIT.
strokeWidth prop (less granular weight control than Lucide).If your design team uses shadcndesign's Figma kit, replace Lucide frames with a HeroIcons library (publish your own Figma icon library or swap components) so design and code stay aligned.
Style & scope: Massive set (2,500+). Unique variable font axes: FILL (outline↔filled), wght, GRAD, opsz.
DX: Best used as a font (span + CSS font-variation-settings). Inline SVG use is possible, but you lose the variable axes—that's most of the value.
A11y: Backed by thorough Material Design guidance.
License: Apache 2.0.
If you adopt Material Symbols, your design team should maintain a Material Symbols Figma library and phase out Lucide frames in shadcndesign-based files. Expect a forked process.
Style & scope: Purpose-built for dense UIs on a 15×15 grid (~330 icons).
DX: Import from @radix-ui/react-icons.
A11y: The standout: AccessibleIcon utility from Radix enforces proper labeling patterns.
License: MIT.
AccessibleIcon.If you move to Radix Icons, provide designers a Radix-based Figma set (or swap icon instances) inside your shadcndesign file to keep parity.
What: Keep Lucide. Add shadcn components as usual.
Why: Zero friction; perfect parity with shadcndesign's default visuals.
Risk: None. You're opting into outline-only.
What: Install another library (e.g., @heroicons/react), remove Lucide, and edit every copied shadcn component to swap imports. Repeat whenever you add new components.
Why: Single dependency.
Risk: Ongoing maintenance burden; you've effectively forked the library.
What: Keep Lucide default; add specific icons from another set via the shadcn community registry (per-icon SVG components).
Why: Solve gaps fast without new npm deps.
Risk: Style drift (mix of sets/sizes). Designers using shadcndesign may not see the mixed set unless you maintain a mirrored Figma icon library.
What: Pick one library (e.g., HeroIcons). Remove lucide-react. Add needed SVGs via the registry one by one. Whenever you add a shadcn component, swap its icon import to your local icon component.
Why: Consistency without forking an npm dependency.
Design pairing: In shadcndesign, replace Lucide icons with your chosen set (publish a Figma icon library for the team).
Risk: Manual, but controlled. The right kind of friction.
| Feature | Lucide | HeroIcons | Material Symbols | Radix Icons |
|---|---|---|---|---|
| Style | Outline-only | Outline + Solid (+ Mini/Micro) | Variable font (fill/weight/grade/opsz) | Outline (15×15) |
| Size (approx.) | 1,600+ | ~300 | 2,500+ | ~330 |
| Best fit | Default parity with shadcn/ui & shadcndesign | Tailwind-native look with two-state UX | Enterprise control & density (font workflow) | A11y-critical, compact UIs |
| DX notes | strokeWidth prop; tree-shakable | Smaller set; good a11y defaults | Ideal as a font, not inline SVG | AccessibleIcon utility |
| License | ISC | MIT | Apache 2.0 | MIT |
Stick with Lucide. It's the default, it matches shadcndesign's icons in Figma, and it removes overhead.
Go HeroIcons. You get Solid + Outline for clear feedback. Pair it with a HeroIcons Figma library inside your shadcndesign file so design matches code.
If you'll leverage variable axes and can adopt the font workflow, Material Symbols is powerful. Keep in mind you'll need a coordinated Figma icon library (and likely won't use shadcndesign's default Lucide icons).
Choose Radix Icons and enforce labeling with AccessibleIcon. Update your shadcndesign-based Figma library to match.
AccessibleIcon is the cleanest, most explicit pattern.Whatever you pick, treat icons as a single source of truth across design and code: choose one set, wire it into the shadcn components with intention, and keep your Figma library (whether shadcndesign or custom) in lockstep.
Founder @ shadcndesign.com

A practical guide to theming shadcn/ui with CSS variables: OKLCH tokens, dark mode, custom brand palettes, and applying a full theme in minutes.

How shadcn/ui charts actually work: what ChartContainer does, how ChartConfig drives colors, and what breaks when you move from Recharts 2 to Recharts 3.

How shadcn/ui registries actually work: registry.json, URL resolution, styles, and namespaces — explained by a team that runs a production registry.