Migrating to lucide icons

What changes when Apsara replaces @radix-ui/react-icons with lucide, and how to keep the old appearance.

Apsara used to draw its icons with @radix-ui/react-icons. It now draws them with lucide, through an icon registry that lets you replace any icon with your own component.

This is a breaking release: some icons look different, and lucide-react is a new peer dependency. Nothing in your code needs to change to keep working, but the UI does change in the places listed below.

1. Install the peer dependency

1npm install lucide-react

The range is narrow on purpose — >=0.500.0 <0.600.0. lucide is a 0.x package, so a wide range would let your installed version decide how Apsara looks.

@radix-ui/react-icons is no longer a dependency of Apsara. If your own code imports from it, keep it in your own dependencies.

2. Check the icons that changed shape

Eight icons inside Apsara's own components draw a different shape than before. Everything else is the same glyph in a different drawing style.

WhereBefore (radix)After (lucide)What changed
Sidebar collapseTriangleDownIconChevronDownIconA solid triangle becomes a chevron. lucide has no solid caret
Menu and ContextMenu submenu markerin-house TriangleRightIconChevronRightIconA solid triangle becomes a chevron
ChatPanel expandSizeIconExpandIconA different glyph
ChatPanel minimizeMinusIconShrinkIconA dash becomes the matched pair of ExpandIcon
PromptInput stopStopIconSquareIconSolid becomes stroke
DataTable sort ascendingTextAlignTopIconArrowUpNarrowWideIconA different glyph
DataTable and DataView sort descendingTextAlignBottomIconArrowDownWideNarrowIconA different glyph
@raystack/apsara/iconsShoppingBagFilledIconin-house solid SVGShoppingBagIconSolid becomes stroke

Two more are worth a look, though the glyph is nearly the same:

  • DatePicker and RangePicker now use CalendarDaysIcon rather than a plain calendar, so the glyph has day marks inside it.
  • Search clear and Toast error now use CircleXIcon in place of CrossCircledIcon.

3. Expect a 1px size change in some places

Every Apsara icon now renders at 16×16 with strokeWidth={1.5}, which draws the 1px stroke of the design because lucide's viewBox is 24 units wide. The radix icons were intrinsically 15×15.

  • A call site that set no size grows from 15px to 16px.
  • A call site that set a CSS class is unaffected — CSS beats an SVG presentation attribute.
  • A call site that set width/height explicitly is unaffected — your props are applied after Apsara's base values.

4. If you want the radix appearance back

Register the radix icons at <Theme>. Apsara ships no radix preset, so copy this map into your app:

1'use client';
2
3import {
4 ArrowDownIcon,
5 ArrowUpIcon,
6 CalendarIcon,
7 CheckCircledIcon,
8 CheckIcon,
9 ChevronDownIcon,
10 ChevronLeftIcon,
11 ChevronRightIcon,
12 CopyIcon,
13 Cross1Icon,
14 CrossCircledIcon,
15 DotsHorizontalIcon,
16 ExclamationTriangleIcon,
17 FileTextIcon,
18 InfoCircledIcon,
19 MagnifyingGlassIcon,
20 MinusIcon,
21 MixerHorizontalIcon,
22 MoonIcon,
23 PlusIcon,
24 SizeIcon,
25 StopIcon,
26 SunIcon,
27 TableIcon,
28 TextAlignBottomIcon,
29 TextAlignTopIcon
30} from '@radix-ui/react-icons';
31import { Theme, type IconOverrides } from '@raystack/apsara';
32
33export const radixIcons: IconOverrides = {
34 XIcon: Cross1Icon,
35 CircleXIcon: CrossCircledIcon,
36 CircleCheckIcon: CheckCircledIcon,
37 TriangleAlertIcon: ExclamationTriangleIcon,
38 InfoIcon: InfoCircledIcon,
39 CheckIcon: CheckIcon,
40 CopyIcon: CopyIcon,
41 SearchIcon: MagnifyingGlassIcon,
42 ChevronDownIcon: ChevronDownIcon,
43 ChevronLeftIcon: ChevronLeftIcon,
44 ChevronRightIcon: ChevronRightIcon,
45 ArrowUpIcon: ArrowUpIcon,
46 ArrowDownIcon: ArrowDownIcon,
47 PlusIcon: PlusIcon,
48 MinusIcon: MinusIcon,
49 EllipsisIcon: DotsHorizontalIcon,
50 ExpandIcon: SizeIcon,
51 ShrinkIcon: MinusIcon,
52 SquareIcon: StopIcon,
53 CalendarDaysIcon: CalendarIcon,
54 FileTextIcon: FileTextIcon,
55 TableIcon: TableIcon,
56 SlidersHorizontalIcon: MixerHorizontalIcon,
57 ArrowUpNarrowWideIcon: TextAlignTopIcon,
58 ArrowDownWideNarrowIcon: TextAlignBottomIcon,
59 SunIcon: SunIcon,
60 MoonIcon: MoonIcon
61};
62
63export function Providers({ children }: { children: React.ReactNode }) {
64 return <Theme icons={radixIcons}>{children}</Theme>;
65}

Two icons cannot be restored from radix, because radix has no equivalent:

  • ListFilterIcon — the old FilterIcon was an in-house SVG. The lucide shape is very close.
  • The Sidebar collapse caret. The map above sets ChevronDownIcon to the radix chevron, which every other call site also used. Restoring the solid triangle in the sidebar alone would also change Accordion, Select, Combobox, and Breadcrumb, because one name draws one shape everywhere.

You do not have to take the whole map. A partial map changes only the icons it names.

5. Register from a client component

An icon map is an object of functions, and a function cannot cross the boundary from a React Server Component to a Client Component. If your <Theme> sits directly in a server layout today, move it into a providers.tsx file marked 'use client', as shown above.

This constraint is new. It applies to any runtime icon registry, not just this design.

6. Rename nine icons from @raystack/apsara/icons

The ./icons subpath itself keeps working, and is still the entry point to reach when you want icons without the component library. What changes is the names it exports: it used to export the raw in-house SVG assets, and it now exports registry wrappers under their registry keys. Nine of the old names are gone.

Removed nameUse insteadAppearance
BellSlashIconBellOffIconSimilar
BuildingsFilledIconBuilding2IconSolid becomes stroke
CoinIconCoinsIconSimilar
FilterIconListFilterIconSimilar
OrganizationIconBuilding2IconSimilar
ResetIconRotateCcwIconSimilar
ShoppingBagFilledIconShoppingBagIconSolid becomes stroke
SidebarIconPanelLeftIconSimilar
TriangleRightIconChevronRightIconSolid triangle becomes a chevron

BuildingsFilledIcon and OrganizationIcon both become Building2Icon, so they are now one component and one override.

Five names are unchanged, because they were already registry keys: BellIcon (lucide Bell), and the four in-house SVGs lucide cannot draw — CoPilotIcon, CoinColoredIcon, CheckCircleFilledIcon, CrossCircleFilledIcon.

Every other name from the subpath is one of the 243 registry icons, and the same component as the one the package root exports.

Next

See Icons › Usage for the naming rules and the override API, and All icons for the full set.