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.

Amit Yadav
Amit Yadav

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.

Width/height instead of size

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:

ComponentRadix 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.

Use the shadcn CLI to re-add components cleanly

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.

Share this post