packages/codemod/docs/ICONS_MIGRATION.md
This guide covers the migration of Chakra UI v2 icons from @chakra-ui/icons to
react-icons/lu (Lucide icons) in v3.
In v3, the @chakra-ui/icons package has been removed. Icons are now sourced
from the react-icons library, primarily using Lucide icons (react-icons/lu).
The codemod automatically handles icon transformations.
Note: This codemod only transforms @chakra-ui/icons imports. If you use
icons in Button or IconButton components, those have separate codemods that
transform the component APIs:
leftIcon/rightIcon props → icons as
childrenicon prop → icon as childRun all relevant codemods for complete migration.
@chakra-ui/icons is no longer maintained in v3react-icons/lu provides 1,541+ Lucide iconsThe codemod intelligently transforms icons based on whether they have props:
Icon component to preserve style
props (color tokens, boxSize, etc.)v2 (no props):
import { AddIcon } from "@chakra-ui/icons"
;<AddIcon />
v3 (no props - direct usage):
import { LuPlus } from "react-icons/lu"
;<LuPlus />
v2 (with props):
import { AddIcon } from "@chakra-ui/icons"
;<AddIcon boxSize={6} color="blue.500" />
v3 (with props - wrapped):
import { Icon } from "@chakra-ui/react"
import { LuPlus } from "react-icons/lu"
;<Icon as={LuPlus} boxSize={6} color="blue.500" />
All v2 icons are mapped to Lucide icons from react-icons/lu:
| v2 Icon | v3 Icon (Lucide) | Notes |
|---|---|---|
AddIcon | LuPlus | Direct equivalent |
ArrowBackIcon | LuArrowLeft | Directional |
ArrowDownIcon | LuArrowDown | Directional |
ArrowForwardIcon | LuArrowRight | Directional |
ArrowLeftIcon | LuArrowLeft | Same as ArrowBackIcon |
ArrowRightIcon | LuArrowRight | Same as ArrowForwardIcon |
ArrowUpIcon | LuArrowUp | Directional |
ArrowUpDownIcon | LuArrowUpDown | Bi-directional |
AtSignIcon | LuAtSign | Direct equivalent |
AttachmentIcon | LuPaperclip | Semantic match |
BellIcon | LuBell | Direct equivalent |
CalendarIcon | LuCalendar | Direct equivalent |
ChatIcon | LuMessageCircle | Semantic match |
CheckIcon | LuCheck | Direct equivalent |
CheckCircleIcon | LuCheckCircle | Direct equivalent |
ChevronDownIcon | LuChevronDown | Direct equivalent |
ChevronLeftIcon | LuChevronLeft | Direct equivalent |
ChevronRightIcon | LuChevronRight | Direct equivalent |
ChevronUpIcon | LuChevronUp | Direct equivalent |
CloseIcon | LuX | Direct equivalent |
CopyIcon | LuCopy | Direct equivalent |
DeleteIcon | LuTrash2 | Semantic match |
DownloadIcon | LuDownload | Direct equivalent |
DragHandleIcon | LuGripVertical | Semantic match |
EditIcon | LuPencil | Semantic match |
EmailIcon | LuMail | Direct equivalent |
ExternalLinkIcon | LuExternalLink | Direct equivalent |
HamburgerIcon | LuMenu | Direct equivalent |
InfoIcon | LuInfo | Direct equivalent |
InfoOutlineIcon | LuInfo | Same as InfoIcon |
LinkIcon | LuLink | Direct equivalent |
LockIcon | LuLock | Direct equivalent |
MinusIcon | LuMinus | Direct equivalent |
MoonIcon | LuMoon | Direct equivalent |
NotAllowedIcon | LuBan | Prohibition symbol |
PhoneIcon | LuPhone | Direct equivalent |
PlusSquareIcon | LuSquarePlus | Direct equivalent |
QuestionIcon | LuHelpCircle | Semantic match |
QuestionOutlineIcon | LuHelpCircle | Same as QuestionIcon |
RepeatIcon | LuRepeat | Direct equivalent |
RepeatClockIcon | LuRepeat | No clock variant in Lucide |
SearchIcon | LuSearch | Direct equivalent |
Search2Icon | LuSearch | Same as SearchIcon |
SettingsIcon | LuSettings | Direct equivalent |
SmallAddIcon | LuPlus | Same as AddIcon |
SmallCloseIcon | LuX | Same as CloseIcon |
SpinnerIcon | LuLoader2 | Loading spinner |
StarIcon | LuStar | Direct equivalent |
SunIcon | LuSun | Direct equivalent |
TimeIcon | LuClock | Direct equivalent |
TriangleDownIcon | LuTriangle | May need CSS rotation |
TriangleUpIcon | LuTriangle | Default orientation |
UnlockIcon | LuUnlock | Direct equivalent |
UpDownIcon | LuArrowUpDown | Same as ArrowUpDownIcon |
ViewIcon | LuEye | Direct equivalent |
ViewOffIcon | LuEyeOff | Direct equivalent |
WarningIcon | LuAlertTriangle | Semantic match |
WarningTwoIcon | LuAlertCircle | Circle variant |
When icons have no props, they're used directly for cleaner code:
v2:
import { CheckIcon } from "@chakra-ui/icons"
;<CheckIcon />
v3:
import { LuCheck } from "react-icons/lu"
;<LuCheck />
When icons have Chakra props, they're wrapped in Icon component:
v2:
import { AddIcon } from "@chakra-ui/icons"
;<AddIcon boxSize={6} color="blue.500" />
v3:
import { Icon } from "@chakra-ui/react"
import { LuPlus } from "react-icons/lu"
;<Icon as={LuPlus} boxSize={6} color="blue.500" />
v2:
import { AddIcon, EditIcon, DeleteIcon } from '@chakra-ui/icons'
<AddIcon />
<EditIcon />
<DeleteIcon />
v3:
import { LuPlus, LuPencil, LuTrash2 } from 'react-icons/lu'
<LuPlus />
<LuPencil />
<LuTrash2 />
In v3, leftIcon and rightIcon props are removed. Icons are placed as direct
children at the start/end of the button:
v2:
import { AddIcon } from "@chakra-ui/icons"
import { Button } from "@chakra-ui/react"
;<Button leftIcon={<AddIcon />}>Add Item</Button>
v3:
import { Button } from "@chakra-ui/react"
import { LuPlus } from "react-icons/lu"
;<Button>
<LuPlus />
Add Item
</Button>
Icons with props are wrapped in Icon component and placed as children:
v2:
import { AddIcon } from "@chakra-ui/icons"
import { Button } from "@chakra-ui/react"
;<Button leftIcon={<AddIcon boxSize={5} color="blue.500" />}>Add Item</Button>
v3:
import { Button, Icon } from "@chakra-ui/react"
import { LuPlus } from "react-icons/lu"
;<Button>
<Icon as={LuPlus} boxSize={5} color="blue.500" />
Add Item
</Button>
v2:
;<Button rightIcon={<ArrowForwardIcon />}>Next</Button>
v3:
;<Button>
Next
<LuArrowRight />
</Button>
In v3, the icon prop is removed. Icons are placed as direct children:
v2:
import { CloseIcon } from "@chakra-ui/icons"
import { IconButton } from "@chakra-ui/react"
;<IconButton aria-label="Close" icon={<CloseIcon />} />
v3:
import { IconButton } from "@chakra-ui/react"
import { LuX } from "react-icons/lu"
;<IconButton aria-label="Close">
<LuX />
</IconButton>
The codemod automatically deduplicates icons that map to the same Lucide icon.
The Icon component is only imported when needed (when icons have props):
v2:
import {
AddIcon,
ArrowBackIcon,
ArrowLeftIcon,
SmallAddIcon,
} from "@chakra-ui/icons"
<AddIcon />
<SmallAddIcon />
<ArrowLeftIcon />
<ArrowBackIcon />
v3 (no props - no Icon import):
import { LuArrowLeft, LuPlus } from "react-icons/lu"
<LuPlus />
<LuPlus />
<LuArrowLeft />
<LuArrowLeft />
// LuPlus used for both AddIcon and SmallAddIcon
// LuArrowLeft used for both ArrowLeftIcon and ArrowBackIcon
All Chakra style props are preserved on the Icon wrapper:
| Prop Type | Example | Works in v3? |
|---|---|---|
| Color tokens | color="blue.500" | ✅ Yes |
| Size utilities | boxSize={6} | ✅ Yes |
| Width/Height | w={8} h={8} | ✅ Yes |
| Responsive | boxSize={[4, 6, 8]} | ✅ Yes |
| Pseudo props | _hover={{ color: "red" }} | ✅ Yes |
| Other props | className, style, etc. | ✅ Yes |
This example shows the full v2 to v3 transformation including Button and IconButton changes. Note that in v3:
leftIcon/rightIcon props → icons as start/end childrenicon prop → icon as direct childIcon componentimport {
AddIcon,
CheckIcon,
CloseIcon,
DeleteIcon,
EditIcon,
} from "@chakra-ui/icons"
import { Box, Button, IconButton, Stack } from "@chakra-ui/react"
function ToolbarExample() {
return (
<Stack direction="row" spacing={4}>
<Button leftIcon={<AddIcon />} colorScheme="blue">
Add Item
</Button>
<IconButton aria-label="Edit" icon={<EditIcon />} colorScheme="green" />
<IconButton aria-label="Delete" icon={<DeleteIcon />} colorScheme="red" />
<Box>
<CheckIcon boxSize={6} color="green.500" />
<CloseIcon boxSize={6} color="red.500" />
</Box>
</Stack>
)
}
import { Box, Button, Icon, IconButton, Stack } from "@chakra-ui/react"
import { LuCheck, LuPencil, LuPlus, LuTrash2, LuX } from "react-icons/lu"
function ToolbarExample() {
return (
<Stack direction="row" spacing={4}>
<Button colorScheme="blue">
<LuPlus />
Add Item
</Button>
<IconButton aria-label="Edit" colorScheme="green">
<LuPencil />
</IconButton>
<IconButton aria-label="Delete" colorScheme="red">
<LuTrash2 />
</IconButton>
<Box>
<Icon as={LuCheck} boxSize={6} color="green.500" />
<Icon as={LuX} boxSize={6} color="red.500" />
</Box>
</Stack>
)
}
npx @chakra-ui/codemod@latest --transform icons src/**/*.tsx
After running the codemod, ensure react-icons is installed:
# npm
npm install react-icons
# yarn
yarn add react-icons
# pnpm
pnpm add react-icons
After running the codemod, review:
Triangle Icons: TriangleDownIcon uses LuTriangle which may need CSS
rotation for proper downward direction:
// May need manual adjustment
<Icon as={LuTriangle} transform="rotate(180deg)" />
Spinner Animation: SpinnerIcon maps to LuLoader2. You may need to add
animation:
<Icon as={LuLoader2} animation="spin 1s linear infinite" />
Icon Variants: Some icons like RepeatClockIcon don't have exact Lucide
equivalents. Review these mappings:
RepeatClockIcon → LuRepeat (no clock variant)InfoOutlineIcon → LuInfo (same as InfoIcon)QuestionOutlineIcon → LuHelpCircle (same as QuestionIcon)react-icons Installation: Verify react-icons is installed as a
dependency
Custom Icon Usage: If icons were used in advanced patterns (HOCs, dynamic rendering, custom wrappers), verify behavior
While the codemod uses Lucide icons (react-icons/lu) by default, you can
manually use other react-icons libraries:
react-icons/md (2,100+ icons)react-icons/hi (316 icons)react-icons/fa (2,048+ icons)react-icons/fi (286 icons)react-icons/imExample:
import { Icon } from "@chakra-ui/react"
import { MdHome } from "react-icons/md"
;<Icon as={MdHome} boxSize={6} />
If you need custom icons, use the createIcon utility:
import { createIcon } from '@chakra-ui/react'
export const CustomIcon = createIcon({
displayName: 'CustomIcon',
viewBox: '0 0 24 24',
path: (
<path
fill="currentColor"
d="M12 2L2 7v10c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V7l-10-5z"
/>
),
})
// Usage
<CustomIcon boxSize={6} color="blue.500" />
Note: Chakra UI v3 includes 19 internal icons used by components, but these are not exported for public use:
These are only for internal component use. For your application, use
react-icons or createIcon().
Rule: Use the Icon wrapper only when you need Chakra-specific style props.
// ✅ No Chakra props? Use icon directly as child (cleaner)
<Button>
<LuPlus />
Add
</Button>
// ✅ Has Chakra props? Wrap in Icon component
<Button>
<Icon as={LuPlus} boxSize={5} color="blue.500" />
Add
</Button>
// ❌ Don't wrap unnecessarily
<Button>
<Icon as={LuPlus} />
Add
</Button>
Remember these API changes when migrating:
// ❌ v2 API - leftIcon/rightIcon props
<Button leftIcon={<LuPlus />}>Add</Button>
<IconButton icon={<LuX />} />
// ✅ v3 API - icons as children
<Button><LuPlus /> Add</Button>
<IconButton><LuX /></IconButton>
Problem: Icons used directly from react-icons don't support Chakra style
props like color="blue.500".
Solution: Wrap icons in Icon component when you need Chakra props:
// ❌ Wrong - Chakra props won't work on react-icons directly
<LuPlus color="blue.500" />
// ✅ Correct - Wrap in Icon to use Chakra props
<Icon as={LuPlus} color="blue.500" />
// ✅ Also correct - No Chakra props needed
<LuPlus />
as propProblem: TypeScript complains about the as prop type.
Solution: Ensure you're passing a component reference (not JSX):
// ❌ Wrong - JSX element
<Icon as={<LuPlus />} />
// ✅ Correct - Component reference
<Icon as={LuPlus} />
Problem: Multiple icons with similar names from different libraries.
Solution: Use named imports with aliases:
import { LuPlus } from "react-icons/lu"
import { MdAdd as MdAddIcon } from "react-icons/md"