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