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.