Radix Icons with shadcn/ui: The Complete Guide
How to use @radix-ui/react-icons with shadcn/ui, full icon list, mixing with Lucide, replacing defaults, and building icon-consistent shadcn components.
When you run npx shadcn@latest init, the default shadcn components ship with @radix-ui/react-icons (a compact set of 318 minimal icons designed by the Radix team to complement their unstyled component system. Most developers ignore it and immediately reach for Lucide, which is fine), but understanding Radix Icons prevents the visual inconsistency that happens when you mix them unknowingly.
This guide covers everything: the full icon list, the Radix-vs-Lucide visual difference, when to mix, and how to build a consistent icon layer on top of shadcn.
What is @radix-ui/react-icons?
@radix-ui/react-icons is a collection of 318 icons built specifically for Radix’s component system, the same primitives that power shadcn/ui. The icons are:
- MIT licensed
- All 15×15px viewBox (not 24×24 like Lucide/Tabler)
- Fill-based, not stroke-based
- Designed to match Radix’s minimal, borderless aesthetic
Installation
npm install @radix-ui/react-icons
It’s a peer dependency of shadcn/ui, if you’ve run shadcn init, it’s already in your node_modules.
Basic usage
import { MagnifyingGlassIcon, BellIcon, GearIcon } from '@radix-ui/react-icons';
export function SearchBar() {
return (
<div className="relative">
<MagnifyingGlassIcon className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground" />
<input className="pl-9 ..." placeholder="Search..." />
</div>
);
}
All Radix icons are named with a Icon suffix and accept standard SVG props plus width, height, and className.
Unlike Lucide’s size prop, Radix icons use width and height separately (defaulting to 15). Use className="w-4 h-4" for Tailwind sizing. It overrides the default cleanly.
Where shadcn uses Radix Icons by default
Several shadcn components ship with Radix Icons baked in:
| Component | Radix Icon used |
|---|---|
<Accordion> | ChevronDownIcon |
<Checkbox> | CheckIcon |
<RadioGroup> | DotFilledIcon |
<Select> | ChevronDownIcon, CheckIcon |
<DropdownMenu> | DotFilledIcon, CheckIcon |
<AlertDialog> | None by default |
<Command> | MagnifyingGlassIcon |
<Popover> | None by default |
These are in the generated component files in src/components/ui/, they’re yours to edit.
The visual difference: Radix vs Lucide
Radix Icons are fill-based on a 15px grid. Lucide is stroke-based on a 24px grid. Mixed in the same UI, they look mismatched:
// Inconsistent, different visual language
import { CheckIcon } from '@radix-ui/react-icons'; // 15px, fill
import { Check } from 'lucide-react'; // 24px, stroke
// In a form field:
<CheckIcon className="w-4 h-4" /> // looks blocky at 16px
<Check size={16} strokeWidth={2.5} /> // looks lighter at 16px
The gap closes at 12px (both look like dots), widens at 20px+ (Lucide is noticeably more refined at larger sizes).
Rule of thumb: If your product uses Lucide as primary, replace the Radix Icon usage in shadcn components with Lucide equivalents. If you’re staying radix-native, keep them and don’t mix.
Replacing Radix Icons with Lucide in shadcn components
After running npx shadcn@latest add select, open src/components/ui/select.tsx and swap the icons:
// Before (default shadcn)
import { ChevronDownIcon, ChevronUpIcon } from "@radix-ui/react-icons"
// After (Lucide)
import { ChevronDown, ChevronUp, Check } from "lucide-react"
// In the component JSX, update the icon usage:
// <ChevronDownIcon className="h-4 w-4 opacity-50" />
// becomes:
// <ChevronDown size={16} className="opacity-50" />
Do the same for Checkbox, RadioGroup, DropdownMenu, and Command to achieve a fully consistent Lucide icon system across all shadcn components.
If you haven’t customized a component yet, delete it and re-add with npx shadcn@latest add select after you’ve established your icon preference. It’s faster than manual find/replace.
Full icon list: the 318 Radix Icons
Radix Icons cover UI actions, navigation, symbols, and typography:
Browse all 318 at the Radix Icons site or search them by pack prefix radix-icons: on AllSVGIcons.
Building an icon layer for shadcn projects
Rather than deciding icon-by-icon, create a thin abstraction that unifies Radix and Lucide:
// src/components/ui/icons.tsx
// Centralize all icon usage, makes swapping libraries painless
export { Check, ChevronDown, ChevronUp, ChevronRight } from 'lucide-react';
export { MagnifyingGlassIcon as Search } from '@radix-ui/react-icons';
// Or go all-Lucide:
export {
Check,
ChevronDown,
ChevronUp,
ChevronRight,
Search,
X,
Circle,
Dot,
} from 'lucide-react';
Then update every shadcn component to import from @/components/ui/icons instead of either library directly.
Using Radix Icons with cn() and class variance authority
Radix Icons don’t have a size prop, so use cn() with Tailwind sizing classes:
import { cn } from '@/lib/utils';
import { GearIcon } from '@radix-ui/react-icons';
interface IconProps {
className?: string;
}
function Settings({ className }: IconProps) {
return <GearIcon className={cn("w-4 h-4 text-muted-foreground", className)} />;
}
Adding icons that Radix doesn’t have
Radix Icons’s 318-icon set has gaps. When you need an icon Radix doesn’t cover, use createLucideIcon to create a custom icon that matches Lucide’s visual language, or import from Lucide:
// Lucide has ~1,500 icons, fill the gaps here
import { BarChart2, Database, Shield, Zap } from 'lucide-react';
Since Lucide uses currentColor on strokes and Radix uses currentColor on fills, both adapt identically to Tailwind text-* and dark:text-* classes.
Frequently asked questions
Do Radix Icons work in Next.js App Router?
Yes, they’re static SVG components with no client-side state. Import and use them in Server Components without a 'use client' directive.
Can I use Radix Icons without shadcn/ui?
Yes. @radix-ui/react-icons is a standalone package. You don’t need any other Radix package to use it.
What’s the file size impact? Each Radix icon is ~0.3–0.6KB. The library tree-shakes identically to Lucide. A typical shadcn app using 10–20 icons adds ~8KB pre-gzip.
Why are Radix Icons 15×15 instead of 24×24? The 15px grid makes icons crisp at 15px/30px sizes (matching Radix’s own component sizes). At 24px+ they look slightly soft, another reason to switch to Lucide for larger display icons.