Documentation for migrating to Chakra UI v3.
# Migration to v3
:::warning
We recommend using the [LLMs.txt](/docs/get-started/llms) files to make the
Chakra UI v3 documentation available to large language models.
:::
## Codemod (Recommended)
The codemod automates the migration from Chakra UI v2 to v3. It handles
component renames, prop changes, import updates, and compound component
restructuring. Start here before migrating manually.
```bash
npx @chakra-ui/codemod upgrade
```
Use `--dry` to preview changes without modifying files.
## Manual Steps
> The minimum node version required is Node.20.x
:::steps
### Update Packages
Remove the unused packages: `@emotion/styled` and `framer-motion`. These
packages are no longer required in Chakra UI.
```bash
npm uninstall @emotion/styled framer-motion
```
Install updated versions of the packages: `@chakra-ui/react` and
`@emotion/react`.
```bash
npm install @chakra-ui/react@latest @emotion/react@latest
```
Next, install component snippets using the CLI snippets. Snippets provide
pre-built compositions of Chakra components to save you time and put you in
charge.
```bash
npx @chakra-ui/cli snippet add
```
### Refactor Custom Theme
Move your custom theme to a dedicated `theme.js` or `theme.ts` file. Use
`createSystem` and `defaultConfig` to configure your theme.
**Before**
```ts
import { extendTheme } from "@chakra-ui/react"
export const theme = extendTheme({
fonts: {
heading: `'Figtree', sans-serif`,
body: `'Figtree', sans-serif`,
},
})
```
**After**
```ts {3}
import { createSystem, defaultConfig } from "@chakra-ui/react"
export const system = createSystem(defaultConfig, {
theme: {
tokens: {
fonts: {
heading: { value: `'Figtree', sans-serif` },
body: { value: `'Figtree', sans-serif` },
},
},
},
})
```
> All token values need to be wrapped in an object with a **value** key. Learn
> more about tokens [here](/docs/theming/tokens).
### Update ChakraProvider
Update the ChakraProvider import from `@chakra-ui/react` to the one from the
snippets. Next, rename the `theme` prop to `value` to match the new system-based
theming approach.
**Before**
```tsx
import { ChakraProvider } from "@chakra-ui/react"
export const App = ({ Component }) => (
)
```
**After**
```tsx {1,3}
import { Provider } from "@/components/ui/provider"
import { defaultSystem } from "@chakra-ui/react"
export const App = ({ Component }) => (
)
```
```tsx {1,3}
import { ColorModeProvider } from "@/components/ui/color-mode"
import { ChakraProvider, defaultSystem } from "@chakra-ui/react"
export function Provider(props) {
return (
)
}
```
> If you have a custom theme, replace `defaultSystem` with the custom `system`
The Provider component compose the `ChakraProvider` from Chakra and
`ThemeProvider` from `next-themes`
:::
## Improvements
- **Performance:** Improved reconciliation performance by `4x` and re-render
performance by `1.6x`
- **Namespaced imports:** Import components using the dot notation for more
concise imports
```tsx
import { Accordion } from "@chakra-ui/react"
const Demo = () => {
return (
)
}
```
- **TypeScript:** Improved IntelliSense and type inference for style props and
tokens.
- **Polymorphism:** Loosened the `as` prop typings in favor of using the
`asChild` prop. This pattern was inspired by Radix Primitives and Ark UI.
## Removed Features
### Color Mode
- `ColorModeProvider` and `useColorMode` have been removed in favor of
`next-themes`
- `LightMode`, `DarkMode` and `ColorModeScript` components have been removed.
You now have to use `className="light"` or `className="dark"` to force themes.
- `useColorModeValue` has been removed in favor of `useTheme` from `next-themes`
:::note
We provide snippets for color mode via the CLI to help you set up color mode
quickly using `next-themes`
:::
### Hooks
We removed the hooks package in favor of using dedicated, robust libraries like
`react-use` and `usehooks-ts`
The only hooks we ship now are `useBreakpointValue`, `useCallbackRef`,
`useDisclosure`, `useControllableState` and `useMediaQuery`.
### Style Config
We removed the `styleConfig` and `multiStyleConfig` concept in favor of recipes
and slot recipes. This pattern was inspired by Panda CSS.
### Next.js package
We've removed the `@chakra-ui/next-js` package in favor of using the `asChild`
prop for better flexibility.
To style the Next.js image component, use the `asChild` prop on the `Box`
component.
```jsx
```
To style the Next.js link component, use the `asChild` prop on the `Link`
component
```jsx
```
### Theme Tools
We've removed this package in favor using CSS color mix.
**Before**
We used JS to resolve the colors and then apply the transparency
```jsx
defineStyle({
bg: transparentize("blue.200", 0.16)(theme),
// -> rgba(0, 0, 255, 0.16)
})
```
**After**
We now use CSS color-mix
```jsx
defineStyle({
bg: "blue.200/16",
// -> color-mix(in srgb, var(--chakra-colors-200), transparent 16%)
})
```
### forwardRef
Due to the simplification of the `as` prop, we no longer provide a custom
`forwardRef`. Prefer to use `forwardRef` from React directly.
Before:
```tsx {3}
import { Button as ChakraButton, forwardRef } from "@chakra-ui/react"
const Button = forwardRef(function Button(props, ref) {
return
})
```
After:
```tsx {2, 4}
import { Button as ChakraButton } from "@chakra-ui/react"
import { forwardRef } from "react"
const Button = forwardRef(
function Button(props, ref) {
return
},
)
```
### Icons
Removed `@chakra-ui/icons` package. Use `react-icons` (Lucide icons recommended)
or `lucide-react` instead. Install with `npm install react-icons`.
- **Icons without props** → use the react-icon directly
- **Icons with Chakra style props** → wrap in `Icon` from `@chakra-ui/react`
Before:
```tsx
import { AddIcon, CheckIcon } from "@chakra-ui/icons"
```
After:
```tsx
import { Icon } from "@chakra-ui/react"
import { LuCheck, LuPlus } from "react-icons/lu"
```
Common icon mappings: `AddIcon` → `LuPlus`, `CloseIcon` → `LuX`, `CheckIcon` →
`LuCheck`, `EditIcon` → `LuPencil`, `DeleteIcon` → `LuTrash2`, `SearchIcon` →
`LuSearch`, `ChevronDownIcon` → `LuChevronDown`, `ArrowForwardIcon` →
`LuArrowRight`, `HamburgerIcon` → `LuMenu`, `WarningIcon` → `LuAlertTriangle`,
`InfoIcon` → `LuInfo`, `ExternalLinkIcon` → `LuExternalLink`, `StarIcon` →
`LuStar`
### Storybook Addon
We're removed the storybook addon in favor of using `@storybook/addon-themes`
and `withThemeByClassName` helper.
```tsx
import { ChakraProvider, defaultSystem } from "@chakra-ui/react"
import { withThemeByClassName } from "@storybook/addon-themes"
import type { Preview, ReactRenderer } from "@storybook/react"
const preview: Preview = {
decorators: [
withThemeByClassName({
defaultTheme: "light",
themes: {
light: "",
dark: "dark",
},
}),
(Story) => (
),
],
}
export default preview
```
### Removed Components
- **StackItem**: You don't need this anymore. Use `Box` instead.
- **FocusLock**: We no longer ship a focus lock component. Install and use
`react-focus-lock` directly.
- **AlertDialog**
- Replace with the `Dialog` component and set `role=alertdialog`
- Set `leastDestructiveRef` prop to the `initialFocusEl` to the `Dialog.Root`
component
### CircularProgress
- Renamed to `ProgressCircle` and now uses compound components
- `isIndeterminate` becomes `value={null}`
- `thickness` prop becomes `--thickness` CSS variable
- `color` prop becomes `stroke` prop on `ProgressCircle.Range`
Before:
```tsx
```
After:
```tsx
```
For indeterminate progress:
```tsx
```
### StackDivider
- No longer available as a separate component
- Use explicit `Stack.Separator` components between stack items
Before:
```tsx
} spacing={4}>
Item 1
Item 2
Item 3
```
After:
```tsx
Item 1
Item 2
Item 3
```
## Prop Changes
### Boolean Props
Changed naming convention for boolean properties from `is` to ``
- `isOpen` -> `open`
- `defaultIsOpen` -> `defaultOpen`
- `isDisabled` -> `disabled`
- `isInvalid` -> `invalid`
- `isRequired` -> `required`
### ColorScheme Prop
The `colorScheme` prop has been changed to `colorPalette`
**Before**
- You could only use `colorScheme` in a component's theme
- `colorScheme` clashes with the native `colorScheme` prop in HTML elements
```tsx
```
**After**
- You can now use `colorPalette` anywhere
```tsx
```
Usage in any component, you can do something like:
```tsx
Some box
Some text
```
If you are using custom colors, you must define two things to make
`colorPalette` work:
- **tokens**: For the 50-950 color palette
- **semanticTokens**: For the `solid`, `contrast`, `fg`, `muted`, `subtle`,
`emphasized`, and `focusRing` color keys
```tsx title="theme.ts" /brand: {/ /tokens: {/ /semanticTokens: {/
import { createSystem, defaultConfig } from "@chakra-ui/react"
export const system = createSystem(defaultConfig, {
theme: {
tokens: {
colors: {
brand: {
50: { value: "#e6f2ff" },
100: { value: "#e6f2ff" },
200: { value: "#bfdeff" },
300: { value: "#99caff" },
// ...
950: { value: "#001a33" },
},
},
},
semanticTokens: {
colors: {
brand: {
solid: { value: "{colors.brand.500}" },
contrast: { value: "{colors.brand.100}" },
fg: { value: "{colors.brand.700}" },
muted: { value: "{colors.brand.100}" },
subtle: { value: "{colors.brand.200}" },
emphasized: { value: "{colors.brand.300}" },
focusRing: { value: "{colors.brand.500}" },
},
},
},
},
})
```
> Read more about it [here](/guides/theming-custom-colors).
### Gradient Props
Gradient style prop simplified to `gradient` and `gradientFrom` and `gradientTo`
props. This reduces the runtime performance cost of parsing the gradient string,
and allows for better type inference.
**Before**
```tsx
```
**After**
```tsx
```
### Color Palette
- Default color palette is now `gray` for all components but you can configure
this in your theme.
- Default theme color palette size has been increased to 11 shades to allow more
color variations.
**Before**
```tsx
const colors = {
// ...
gray: {
50: "#F7FAFC",
100: "#EDF2F7",
200: "#E2E8F0",
300: "#CBD5E0",
400: "#A0AEC0",
500: "#718096",
600: "#4A5568",
700: "#2D3748",
800: "#1A202C",
900: "#171923",
},
}
```
**After**
```tsx
const colors = {
// ...
gray: {
50: { value: "#fafafa" },
100: { value: "#f4f4f5" },
200: { value: "#e4e4e7" },
300: { value: "#d4d4d8" },
400: { value: "#a1a1aa" },
500: { value: "#71717a" },
600: { value: "#52525b" },
700: { value: "#3f3f46" },
800: { value: "#27272a" },
900: { value: "#18181b" },
950: { value: "#09090b" },
},
}
```
### Style Props
Changed the naming convention for some style props
- `noOfLines` -> `lineClamp`
- `truncated` -> `truncate`
- `_activeLink` -> `_currentPage`
- `_activeStep` -> `_currentStep`
- `_mediaDark` -> `_osDark`
- `_mediaLight` -> `_osLight`
**Examples:**
```tsx
// Before
Long text that will be clamped to 2 lines
This text will be truncated with ellipsis
// After
Long text that will be clamped to 2 lines
This text will be truncated with ellipsis
```
We removed the `apply` prop in favor of `textStyle` or `layerStyles`
### Nested Styles
We have changed the way you write nested styles in Chakra UI components.
**Before**
Write nested styles using the `sx` or `__css` prop, and you sometimes don't get
auto-completion for nested styles.
```tsx
```
**After**
Write nested styles using the `css` prop. All nested selectors **require** the
use of the ampersand `&` prefix
```tsx
```
This was done for two reasons:
- **Faster style processing:** Before we had to check if a style key is a style
prop or a selector which is quite expensive overall.
- **Better typings:** This makes it easier to type nested style props are
strongly typed
## Component Changes
### ChakraProvider
- Removed `theme` prop in favor of passing the `system` prop instead. Import the
`defaultSystem` module instead of `theme`
- Removed `resetCss` prop in favor of passing `preflight: false` to the
`createSystem` function
Before
```tsx
```
After
```tsx
const system = createSystem(defaultConfig, { preflight: false })
```
- Removed support for configuring toast options. Pass it to the `createToaster`
function in `components/ui/toaster.tsx` file instead.
### Modal
Renamed to `Dialog` and uses compound components with an explicit
`Dialog.Positioner` and `Portal` wrapper.
**Component Renaming:**
- `Modal` → `Dialog.Root`
- `ModalOverlay` → `Dialog.Backdrop`
- `ModalContent` → `Dialog.Content` (wrap in `Dialog.Positioner`)
- `ModalHeader` → `Dialog.Header`
- `ModalBody` → `Dialog.Body`
- `ModalFooter` → `Dialog.Footer`
- `ModalCloseButton` → `Dialog.CloseTrigger`
**Prop Changes:**
- `isOpen` → `open`
- `onClose` → `onOpenChange` (receives `{ open }`)
- `isCentered` → `placement="center"`
- `closeOnOverlayClick` → `closeOnInteractOutside`
- `closeOnEsc` → `closeOnEscape`
- `blockScrollOnMount` → `preventScroll`
- `onOverlayClick` → `onInteractOutside`
- `onEsc` → `onEscapeKeyDown`
- `onCloseComplete` → `onExitComplete`
- `initialFocusRef` → `initialFocusEl={() => ref.current}`
- `finalFocusRef` → `finalFocusEl={() => ref.current}`
**Size Mapping:** Sizes `2xl` through `6xl` are mapped to `xl` in v3.
**Removed Props:** `allowPinchZoom`, `lockFocusAcrossFrames`,
`preserveScrollBarGap`, `returnFocusOnClose`, `useInert`, `portalProps`
### Avatar
Now uses a declarative composition pattern with separate `Avatar.Image` and
`Avatar.Fallback` parts.
**Component Renaming:**
- `Avatar` → `Avatar.Root`
- `AvatarBadge` → removed (use `Float` + `Circle` instead)
- `AvatarGroup` → `AvatarGroup` (unchanged, but `max` prop removed)
**Props moved to `Avatar.Image`:**
- `src`, `srcSet`, `sizes`, `loading`, `referrerPolicy`, `crossOrigin`
**Props moved to `Avatar.Fallback`:**
- `name` — generates initials automatically
- `icon` — render as children instead
- `iconLabel` → `aria-label`
**Props removed:**
- `ignoreFallback` — no longer needed
- `showBorder` — use `border` and `borderColor` style props instead
- `AvatarGroup` `max` — removed, handle in userland
- `AvatarGroup` `spacing` → `spaceX`
Before:
```tsx
import { Avatar, AvatarBadge, AvatarGroup } from "@chakra-ui/react"
const Demo = () => (
<>
} />
>
)
```
After:
```tsx
import { Avatar, AvatarGroup, Circle, Float } from "@chakra-ui/react"
const Demo = () => (
<>
>
)
```
### Breadcrumb
Now uses compound components with explicit separators between items and a
required `Breadcrumb.List` wrapper.
**Component Renaming:**
- `Breadcrumb` → `Breadcrumb.Root`
- `BreadcrumbItem` → `Breadcrumb.Item`
- `BreadcrumbLink` → `Breadcrumb.Link`
- `BreadcrumbLink` with `isCurrentPage` → `Breadcrumb.CurrentLink`
- `BreadcrumbSeparator` → `Breadcrumb.Separator`
**Prop Changes:**
- `separator` prop → removed, use explicit `` between
items
- `spacing` → `gap` (moved to `Breadcrumb.List`)
- `isCurrentPage` on `BreadcrumbItem` → use `Breadcrumb.CurrentLink` instead
- `isLastChild` → removed (not needed with explicit separators)
- `listProps` → spread directly on `Breadcrumb.List`
Before:
```tsx
import { Breadcrumb, BreadcrumbItem, BreadcrumbLink } from "@chakra-ui/react"
const Demo = () => (
Home
Current
)
```
After:
```tsx
import { Breadcrumb } from "@chakra-ui/react"
const Demo = () => (
Home
-
Current
)
```
### Portal
- Remove `appendToParentPortal` prop in favor of using the `containerRef`
- Remove `PortalManager` component
### Progress
- Now uses compound components with `Progress.Root`, `Progress.Track`, and
`Progress.Range`
- `hasStripe` prop renamed to `striped`
- `isAnimated` prop renamed to `animated`
- `colorScheme` prop renamed to `colorPalette`
Before:
```tsx
```
After:
```tsx
```
### Stack
- Changed `spacing` to `gap`
- Removed `StackItem` in favor of using the `Box` component directly
### Select
Now called `NativeSelect` and exposes all parts now.
Before:
```tsx
```
After:
```tsx
```
Changing the icon
Before:
```tsx
} placeholder="Woohoo! A new icon" />
```
After:
```tsx
```
### Collapse
- Rename `Collapse` to `Collapsible` namespace
- Rename `in` to `open`
- `animateOpacity` has been removed, use keyframes animations `expand-height`
and `collapse-height` instead
Before
```tsx
Some content
```
After
```tsx
Some content
```
### Image
Now renders a native `img` without built-in fallback logic. `Img` has been
consolidated into `Image`.
- `Img` → `Image`
- `fit` → `objectFit`
- `align` → `objectPosition`
- `fallbackSrc`, `fallback`, `ignoreFallback`, `fallbackStrategy` → removed
- `useImage` hook → removed
Before:
```tsx
import { Img } from "@chakra-ui/react"
const Demo = () => (
)
```
After:
```tsx
import { Image } from "@chakra-ui/react"
const Demo = () => (
)
```
> For fallback behavior, use the native `onError` event to swap the `src`.
### PinInput
Now uses compound components. Each input requires an `index` prop and must be
wrapped in `PinInput.Control`.
**Component Renaming:**
- `PinInput` → `PinInput.Root`
- `PinInputField` → `PinInput.Input` (requires `index` prop)
**Prop Changes:**
- `value` / `defaultValue` → now `string[]` instead of `string`
- `onChange` → `onValueChange` (receives `{ value, valueAsString }`)
- `onComplete` → `onValueComplete` (receives `{ value, valueAsString }`)
- `isDisabled` → `disabled`
- `isInvalid` → `invalid`
- `manageFocus` → removed
Before:
```tsx
import { PinInput, PinInputField } from "@chakra-ui/react"
const Demo = () => (
)
```
After:
```tsx
import { PinInput } from "@chakra-ui/react"
const Demo = () => (
setValue(e.value)}
onValueComplete={(e) => handleComplete(e.value)}
>
)
```
### Popover
Now uses compound components with an explicit `Popover.Positioner` wrapper
around content. `PopoverTrigger` now requires `asChild`.
**Component Renaming:**
- `Popover` → `Popover.Root`
- `PopoverTrigger` → `Popover.Trigger` (add `asChild`)
- `PopoverContent` → `Popover.Content` (wrap in `Popover.Positioner`)
- `PopoverHeader` → `Popover.Title`
- `PopoverBody` → `Popover.Body`
- `PopoverFooter` → `Popover.Footer`
- `PopoverArrow` → `Popover.Arrow`
- `PopoverCloseButton` → `Popover.CloseTrigger`
- `PopoverAnchor` → `Popover.Anchor`
**Prop Changes:**
- `isOpen` → `open`
- `defaultIsOpen` → `defaultOpen`
- `onClose` / `onOpen` → `onOpenChange` (receives `{ open }`)
- `closeOnBlur` → `closeOnInteractOutside`
- `closeOnEsc` → `closeOnEscape`
- `isLazy` → `lazyMount`
- `lazyBehavior="unmount"` → `unmountOnExit`
- `initialFocusRef` → `initialFocusEl={() => ref.current}`
- `trigger="hover"` → use `HoverCard` component instead
- Positioning props (`placement`, `gutter`, `flip`, `offset`, `matchWidth`,
`strategy`) → grouped into `positioning` object
- `matchWidth` → `positioning.sameWidth`
**Removed Props:** `computePositionOnMount`, `returnFocusOnClose`,
`arrowShadowColor`, `modifiers`
Before:
```tsx
import {
Popover,
PopoverArrow,
PopoverBody,
PopoverCloseButton,
PopoverContent,
PopoverHeader,
PopoverTrigger,
} from "@chakra-ui/react"
const Demo = () => (
Title
Content here
)
```
After:
```tsx
import { Popover } from "@chakra-ui/react"
const Demo = () => (
Title
Content here
)
```
**Hover Trigger → HoverCard:**
If you used `trigger="hover"`, migrate to the `HoverCard` component:
Before:
```tsx
Tooltip-like content
```
After:
```tsx
import { HoverCard } from "@chakra-ui/react"
const Demo = () => (
Content here
)
```
### NumberInput
**Component Renaming:**
- `NumberInput` → `NumberInput.Root`
- `NumberInputField` → `NumberInput.Input`
- `NumberInputStepper` → `NumberInput.Control`
- `NumberIncrementStepper` → `NumberInput.IncrementTrigger`
- `NumberDecrementStepper` → `NumberInput.DecrementTrigger`
**Prop Changes:**
- `isDisabled` → `disabled`
- `isInvalid` → `invalid`
- `isReadOnly` → `readOnly`
- `isRequired` → `required`
- `onChange` → `onValueChange` (receives `{ value, valueAsNumber }`)
- `onInvalid` → `onValueInvalid`
- `keepWithinRange` → `allowOverflow` (inverted: `false` → `true`)
- `focusBorderColor` / `errorBorderColor` → use `--focus-color` /
`--error-color` CSS variables
- `parse` and `format` → removed, use `formatOptions` instead
Before:
```tsx
import {
NumberDecrementStepper,
NumberIncrementStepper,
NumberInput,
NumberInputField,
NumberInputStepper,
} from "@chakra-ui/react"
const Demo = () => (
{}}
keepWithinRange={false}
>
)
```
After:
```tsx
import { NumberInput } from "@chakra-ui/react"
const Demo = () => (
{}} allowOverflow>
)
```
### Divider
Renamed to `Separator` to better align with semantic HTML and ARIA standards.
The component now uses a `div` element for better layout control.
- `Divider` → `Separator`
- Relies on `borderTopWidth` and `borderInlineStartWidth` for styling
- To change thickness, set the `--divider-border-width` CSS variable
- All props (`orientation`, `variant`, styling) remain the same
Before:
```tsx
import { Divider } from "@chakra-ui/react"
const Demo = () => (
<>
>
)
```
After:
```tsx
import { Separator } from "@chakra-ui/react"
const Demo = () => (
<>
>
)
```
### Card
Now uses compound components with dot notation. The migration is straightforward
— only component names change, all props remain the same.
**Component Renaming:**
- `Card` → `Card.Root`
- `CardHeader` → `Card.Header`
- `CardBody` → `Card.Body`
- `CardFooter` → `Card.Footer`
v3 also introduces `Card.Title` and `Card.Description` as new semantic
components for better structure.
Before:
```tsx
import {
Button,
Card,
CardBody,
CardFooter,
CardHeader,
Heading,
Text,
} from "@chakra-ui/react"
const Demo = () => (
Living room Sofa
This sofa is perfect for modern tropical spaces.
$450
)
```
After:
```tsx
import { Button, Card, Heading, Text } from "@chakra-ui/react"
const Demo = () => (
Living room Sofa
This sofa is perfect for modern tropical spaces.
$450
)
```
### Input, Select, Textarea
- Removed `invalid` prop in favor of wrapping the component in a `Field`
component. This allows for adding a label, error text and asterisk easily.
Before
```tsx
```
After
```tsx
Email
This field is required
```
### Link
- Removed `isExternal` prop in favor of explicitly setting the `target` and
`rel` props
Before
```tsx
Click me
```
After
```tsx
Click me
```
### List
Now uses compound components with dot notation. `OrderedList` and
`UnorderedList` are no longer separate components — use `List.Root` with the
`as` prop instead.
**Component Renaming:**
- `List` → `List.Root`
- `OrderedList` → `List.Root as="ol"`
- `UnorderedList` → `List.Root as="ul"`
- `ListItem` → `List.Item`
- `ListIcon` → `List.Indicator`
**Prop Changes:**
- `spacing` → `gap`
- `styleType` → `listStyleType`
- `stylePosition` → `listStylePosition`
Before:
```tsx
import { ListIcon, ListItem, UnorderedList } from "@chakra-ui/react"
import { MdCheckCircle } from "react-icons/md"
const Demo = () => (
Lorem ipsum dolor sit amet
Consectetur adipiscing elit
)
```
After:
```tsx
import { List } from "@chakra-ui/react"
import { MdCheckCircle } from "react-icons/md"
const Demo = () => (
Lorem ipsum dolor sit amet
Consectetur adipiscing elit
)
```
For ordered lists:
Before:
```tsx
import { ListItem, OrderedList } from "@chakra-ui/react"
const Demo = () => (
First item
Second item
)
```
After:
```tsx
import { List } from "@chakra-ui/react"
const Demo = () => (
First item
Second item
)
```
### Button
**Prop Changes:**
- `isActive` → `data-active` attribute
- `isDisabled` → `disabled`
- `isLoading` → `loading`
- `colorScheme` → `colorPalette`
- `leftIcon` / `rightIcon` → render icons as children directly
- `iconSpacing` → `gap`
- `variant="unstyled"` → `unstyled` boolean prop
- `variant="link"` → `variant="plain"`
Before:
```tsx
} iconSpacing={2}>
Download
```
After:
```tsx
```
**ButtonGroup Changes:**
- `isAttached` → `attached`
- `isDisabled` → removed (propagate `disabled` to each child instead)
### IconButton
- `icon` → render as `children` directly
- `isRounded` → `borderRadius="full"`
- `isDisabled` → `disabled`
Before:
```tsx
} isRounded isDisabled aria-label="Search" />
```
After:
```tsx
```
### Spinner
- Change the `thickness` prop to `borderWidth`
- Change the `speed` prop to `animationDuration`
Before
```tsx
```
After
```tsx
```
### Dialog, Drawer
Both `Modal` and `Drawer` now use compound components with an explicit
`Positioner` and `Portal` wrapper.
**Prop Changes (shared by Dialog and Drawer):**
- `isOpen` → `open`
- `onClose` → `onOpenChange` (receives `{ open }`)
- `blockScrollOnMount` → `preventScroll`
- `closeOnEsc` → `closeOnEscape`
- `closeOnOverlayClick` → `closeOnInteractOutside`
- `onOverlayClick` → `onInteractOutside`
- `onEsc` → `onEscapeKeyDown`
- `onCloseComplete` → `onExitComplete`
- `initialFocusRef` → `initialFocusEl={() => ref.current}`
- `finalFocusRef` → `finalFocusEl={() => ref.current}`
- `isCentered` → `placement="center"` (Dialog only)
- Sizes `2xl`–`6xl` → mapped to `xl`
**Drawer-specific Changes:**
- `placement="left"` → `placement="start"` (RTL-aware)
- `placement="right"` → `placement="end"` (RTL-aware)
- `isFullHeight` → add `height="100%"` to `Drawer.Content`
- `DrawerOverlay` → `Drawer.Backdrop`
- `DrawerContent` → `Drawer.Positioner` + `Drawer.Content`
**Removed Props:** `allowPinchZoom`, `lockFocusAcrossFrames`,
`preserveScrollBarGap`, `returnFocusOnClose`, `useInert`, `portalProps`
**Dialog Example:**
Before:
```tsx
import {
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
} from "@chakra-ui/react"
const Demo = () => (
Title
Content
)
```
After:
```tsx
import { Dialog, Portal } from "@chakra-ui/react"
const Demo = () => (
!e.open && onClose()}
placement="center"
closeOnEscape={false}
>
Title
Content
)
```
**Drawer Example:**
Before:
```tsx
import {
Drawer,
DrawerBody,
DrawerCloseButton,
DrawerContent,
DrawerFooter,
DrawerHeader,
DrawerOverlay,
} from "@chakra-ui/react"
const Demo = () => (
Title
Content
)
```
After:
```tsx
import { Drawer, Portal } from "@chakra-ui/react"
const Demo = () => (
!e.open && onClose()}
>
Title
Content
)
```
### Editable
Now uses compound components with dot notation. Custom controls use declarative
trigger components instead of the `useEditableControls` prop-getter pattern.
**Component Renaming:**
- `Editable` → `Editable.Root`
- `EditablePreview` → `Editable.Preview`
- `EditableInput` → `Editable.Input`
- `EditableTextarea` → `Editable.Textarea`
- `useEditableControls` → `useEditableContext`
**Prop Changes:**
- `isDisabled` → `disabled`
- `onChange` → `onValueChange` (receives `{ value }` object)
- `onSubmit` → `onValueCommit`
- `onCancel` → `onValueRevert`
- `startWithEditView` → `defaultEdit`
- `selectAllOnFocus` → `selectOnFocus`
- `submitOnBlur={false}` → `submitMode="enter"`
- `finalFocusRef` → `finalFocusEl` (function returning element)
- `isPreviewFocusable={false}` → add `tabIndex={undefined}` to
`Editable.Preview`
Before:
```tsx
import { Editable, EditableInput, EditablePreview } from "@chakra-ui/react"
const Demo = () => (
)
```
After:
```tsx
import { Editable } from "@chakra-ui/react"
const Demo = () => (
)
```
**Custom Controls:**
The `useEditableControls` prop-getter pattern is replaced by declarative trigger
components.
Before:
```tsx
function EditableControls() {
const { isEditing, getSubmitButtonProps, getCancelButtonProps } =
useEditableControls()
return isEditing ? (
} {...getSubmitButtonProps()} />
} {...getCancelButtonProps()} />
) : null
}
```
After:
```tsx
```
### FormControl
Replaced by `Field` for standard form controls and `Fieldset` for grouped
controls (radio groups, checkbox groups). The `as='fieldset'` pattern is
replaced with a dedicated `Fieldset` component.
**Component Renaming:**
- `FormControl` → `Field.Root`
- `FormLabel` → `Field.Label`
- `FormHelperText` → `Field.HelperText`
- `FormErrorMessage` → `Field.ErrorText`
For fieldset usage:
- `FormControl as='fieldset'` → `Fieldset.Root`
- `FormLabel as='legend'` → `Fieldset.Legend`
- `FormHelperText` → `Fieldset.HelperText`
- `FormErrorMessage` → `Fieldset.ErrorText`
**Prop Changes:**
- `isInvalid` → `invalid`
- `isRequired` → `required`
- `isDisabled` → `disabled`
- `isReadOnly` → `readOnly`
Before:
```tsx
import {
FormControl,
FormErrorMessage,
FormHelperText,
FormLabel,
} from "@chakra-ui/react"
const Demo = () => (
Email
We'll never share your email.
Email is required.
)
```
After:
```tsx
import { Field } from "@chakra-ui/react"
const Demo = () => (
Email
We'll never share your email.
Email is required.
)
```
> `Field.ErrorText` only renders when `invalid` is `true`, so no conditional
> logic is needed.
**Fieldset Usage:**
Before:
```tsx
import { FormControl, FormHelperText, FormLabel } from "@chakra-ui/react"
const Demo = () => (
Favorite Character
Select only if you're a fan.
)
```
After:
```tsx
import { Fieldset } from "@chakra-ui/react"
const Demo = () => (
Favorite Character
Select only if you're a fan.
)
```
### Collapse
Replace with the `Collapsible` component.
Before:
```tsx
Some content
```
After:
```tsx
Some content
```
### Fade, ScaleFade, Slide, SlideFade
All transition components have been replaced by a unified `Presence` component
that uses CSS-based animations instead of JavaScript-based transitions.
**Component Mapping:**
- `Fade` → `Presence` with
`animationName={{ _open: "fade-in", _closed: "fade-out" }}`
- `ScaleFade` → `Presence` with
`animationStyle={{ _open: "scale-fade-in", _closed: "scale-fade-out" }}`
- `SlideFade` → `Presence` with
`animationName={{ _open: "slide-from-bottom, fade-in", _closed: "slide-to-bottom, fade-out" }}`
- `Slide` → `Presence` with direction-specific positioning and animation
**Prop Changes:**
- `in` → `present`
- `initialScale` → removed (scale is fixed in CSS keyframes)
- `offsetX` / `offsetY` → removed (offset is fixed in CSS keyframes)
- `direction` → replaced by positioning props and direction-specific animation
names
Before:
```tsx
import { Fade, Slide } from "@chakra-ui/react"
const Demo = () => (
<>
Fading content
Sliding content
>
)
```
After:
```tsx
import { Presence } from "@chakra-ui/react"
const Demo = () => (
<>
Fading content
Sliding content
>
)
```
**Slide Direction Mapping:**
| Direction | Positioning | Open Animation | Close Animation |
| --------- | -------------------------------------------- | ------------------------ | ---------------------- |
| `top` | `position="fixed"` `top="0"` `insetX="0"` | `slide-from-top-full` | `slide-to-top-full` |
| `bottom` | `position="fixed"` `bottom="0"` `insetX="0"` | `slide-from-bottom-full` | `slide-to-bottom-full` |
| `left` | `position="fixed"` `left="0"` `insetY="0"` | `slide-from-left-full` | `slide-to-left-full` |
| `right` | `position="fixed"` `right="0"` `insetY="0"` | `slide-from-right-full` | `slide-to-right-full` |
### Slider / RangeSlider
`RangeSlider` has been unified with `Slider` — pass an array value for range
mode. Both now require a `Slider.Control` wrapper and `Slider.HiddenInput`
inside each thumb.
**Component Renaming:**
- `Slider` / `RangeSlider` → `Slider.Root`
- `SliderTrack` / `RangeSliderTrack` → `Slider.Track`
- `SliderFilledTrack` / `RangeSliderFilledTrack` → `Slider.Range`
- `SliderThumb` / `RangeSliderThumb` → `Slider.Thumb`
**Prop Changes:**
- `onChange` → `onValueChange` (receives `{ value }`)
- `onChangeEnd` → `onValueChangeEnd` (receives `{ value }`)
- `onChangeStart` → removed
- `colorScheme` → `colorPalette`
- `isReversed` / `reversed` → removed (use `dir="rtl"`)
- `focusThumbOnChange` → removed
Before:
```tsx
import {
RangeSlider,
RangeSliderFilledTrack,
RangeSliderThumb,
RangeSliderTrack,
} from "@chakra-ui/react"
const Demo = () => (
console.log(val)}>
)
```
After:
```tsx
import { Slider } from "@chakra-ui/react"
const Demo = () => (
console.log(e.value)}
>
)
```
### Table
- `TableContainer` is now `Table.ScrollArea`
- `Td`(now called `Table.ColumnHeader`) `isNumeric` is now `textAlign="end"`
The compound component have been renamed slightly.
Before:
```tsx
Imperial to metric conversion factors
| Product |
Category |
Price |
{items.map((item) => (
| {item.name} |
{item.category} |
{item.price} |
))}
| Product |
Category |
Price |
```
After:
```tsx
Product
Category
Price
{items.map((item) => (
{item.name}
{item.category}
{item.price}
))}
```
### Tag
`TagLeftIcon` and `TagRightIcon` are now `Tag.StartElement` and `Tag.EndElement`
Before:
```tsx
Cyan
```
After:
```tsx
Cyan
```
- `TagCloseButton` is now `Tag.CloseTrigger`
Before:
```tsx
Green
```
After:
```tsx
Green
```
### Alert
Now uses compound components with dot notation. v3 also introduces
`Alert.Content` as a wrapper for title and description.
**Component Renaming:**
- `Alert` → `Alert.Root`
- `AlertIcon` → `Alert.Indicator`
- `AlertTitle` → `Alert.Title`
- `AlertDescription` → `Alert.Description`
**Prop Changes:**
- Removed `addRole` prop (role is handled automatically in v3)
Before:
```tsx
import {
Alert,
AlertDescription,
AlertIcon,
AlertTitle,
} from "@chakra-ui/react"
const Demo = () => (
Your browser is outdated!
Your Chakra experience may be degraded.
)
```
After:
```tsx
import { Alert } from "@chakra-ui/react"
const Demo = () => (
Your browser is outdated!
Your Chakra experience may be degraded.
)
```
**Variant Changes:**
The `left-accent` and `top-accent` variants have been removed. Replicate them
using border style props on `Alert.Root`:
- `left-accent` → `variant="subtle"` + `borderStartWidth="3px"` +
`borderStartColor="colorPalette.solid"`
- `top-accent` → `variant="subtle"` + `borderTopWidth="3px"` +
`borderTopColor="colorPalette.solid"`
New variants `surface` and `outline` have been added.
Before:
```tsx
Data uploaded to the server. Fire on!
```
After:
```tsx
Data uploaded to the server. Fire on!
```
### Skeleton
- `startColor` and `endColor` props now use CSS variables
Before:
```tsx
```
After:
```tsx
```
- `isLoaded` prop is now `loading`
Before:
```tsx
Chakra ui is cool
```
After:
```tsx
Chakra ui is cool
```
### Stepper
Renamed to `Steps` with compound component pattern. The `useSteps` hook is still
available but with updated API.
**Component Renaming:**
- `Stepper` → `Steps.Root`
- `Step` → `Steps.Item`
- `StepIndicator` → `Steps.Indicator`
- `StepStatus` → `Steps.Status`
- `StepTitle` → `Steps.Title`
- `StepDescription` → `Steps.Description`
- `StepSeparator` → `Steps.Separator`
**Prop Changes:**
- `index` → `step`
- Children must be wrapped in `Steps.List`
**Hook Changes:**
- `useSteps({ index })` → `useSteps({ defaultStep })`
- When using `useSteps`, use `Steps.RootProvider` with `value={stepsApi}`
instead of `Steps.Root`
Before:
```tsx
import {
Step,
StepIcon,
StepIndicator,
StepNumber,
StepSeparator,
StepStatus,
StepTitle,
Stepper,
} from "@chakra-ui/react"
const Demo = () => (
{steps.map((step, index) => (
} incomplete={} />
{step.title}
))}
)
```
After:
```tsx
import { Steps } from "@chakra-ui/react"
const Demo = () => (
{steps.map((step, index) => (
} incomplete={} />
{step.title}
))}
)
```
### Stat
Now uses compound components with dot notation.
**Component Renaming:**
- `Stat` → `Stat.Root`
- `StatLabel` → `Stat.Label`
- `StatNumber` → `Stat.ValueText`
- `StatHelpText` → `Stat.HelpText`
- `StatArrow type="increase"` → `Stat.UpIndicator`
- `StatArrow type="decrease"` → `Stat.DownIndicator`
- `StatGroup` → `Stat.Root` (nest `Stat.Root` children inside)
Before:
```tsx
import {
Stat,
StatArrow,
StatHelpText,
StatLabel,
StatNumber,
} from "@chakra-ui/react"
const Demo = () => (
Revenue
$45,670
12.5%
)
```
After:
```tsx
import { Stat } from "@chakra-ui/react"
const Demo = () => (
Revenue
$45,670
12.5%
)
```
### Menu
- Now uses compound components everywhere
Before:
```tsx
```
After:
```tsx
Download
Create a Copy
```
- Accesing internal state is now done via `Menu.Context` no longer render prop.
Before:
```tsx
```
After:
```tsx
{(menu) => (
)}
Download
alert("Kagebunshin")}>
Create a Copy
```
- `isLazy` prop on `Menu` is split into `lazyMount` and `unmountOnExit` on
`Menu.Root`
- `MenuOptionGroup` is now split into `Menu.RadioItemGroup` and
`Menu.CheckboxItemGroup` to handle the states separately.
Before:
```tsx
```
After:
```tsx
Ascending
Descending
Email
Phone
Country
```
### Tooltip
Now a **snippet** component imported from `@/components/ui/tooltip` instead of
`@chakra-ui/react`.
**Prop Changes:**
- `label` → `content`
- `hasArrow` → `showArrow`
- `closeOnEsc` → `closeOnEscape`
- `closeOnMouseDown` → `closeOnPointerDown`
- `onOpen` / `onClose` → `onOpenChange` (receives `{ open }`)
- `shouldWrapChildren` → wrap children in `` manually
- `placement`, `gutter`, `offset`, `arrowPadding` → grouped into `positioning`
object
**Removed Props:** `modifiers`, `motionProps`, `portalProps`, `arrowSize`,
`arrowShadowColor`
Before:
```tsx
import { Tooltip } from "@chakra-ui/react"
;
```
After:
```tsx
import { Tooltip } from "@/components/ui/tooltip"
;
```
### Accordion
Now uses compound components with dot notation. All sub-components are
namespaced under `Accordion`.
**Component Renaming:**
- `Accordion` → `Accordion.Root`
- `AccordionItem` → `Accordion.Item` (now **requires** a `value` prop)
- `AccordionButton` → `Accordion.ItemTrigger`
- `AccordionIcon` → `Accordion.ItemIndicator`
- `AccordionPanel` → `Accordion.ItemContent` + `Accordion.ItemBody`
**Prop Changes:**
- `allowMultiple` → `multiple`
- `allowToggle` → `collapsible`
- `defaultIndex` → `defaultValue` (now an array of strings)
- `index` → `value` (now an array of strings)
- `onChange` → `onValueChange`
Before:
```tsx
import {
Accordion,
AccordionButton,
AccordionIcon,
AccordionItem,
AccordionPanel,
Box,
} from "@chakra-ui/react"
const Demo = () => (
Section 1 title
Lorem ipsum dolor sit amet.
)
```
After:
```tsx
import { Accordion, Box } from "@chakra-ui/react"
const Demo = () => (
Section 1 title
Lorem ipsum dolor sit amet.
)
```
**Render Props → Context:**
The `AccordionItem` render prop pattern (`{({ isExpanded }) => ...}`) has been
replaced. Use the `Accordion.ItemContext` component or the
`useAccordionItemContext` hook instead. The `isExpanded` property is now `expanded`.
Before:
```tsx
{({ isExpanded }) => (
<>
Section title
{isExpanded ? : }
Content
>
)}
```
After:
```tsx
{({ expanded }) => (
<>
Section title
{expanded ? : }
Content
>
)}
```
### Tabs
- Component structure has changed and `value` prop is now required on list and
panels.
Before:
```tsx
One
Two
Three
one!
two!
three!
```
After:
```tsx
One
Two
Three
one!
two!
three!
```
- `defaultIndex`, `index` and `onChange` is now `defaultValue`, `value` and
`onValueChange` respectively
Before:
```tsx
{}} />
```
After:
```tsx
{}} />
```
- `isLazy` prop on `Tabs` is now `lazyMount` and `unmountOnExit` on `Tabs.Root`
Before:
```tsx
```
After:
```tsx
```
### Show and Hide
- `Show` and `Hide` components are removed in favor of `hideFrom` and
`hideBelow`
Before:
```tsx
This text appears only on screens md and smaller.
This text hides at the "md" value screen width and smaller.
```
After:
```tsx
This text hides at the "md" value screen width and smaller.
This text appears only on screens md and larger.
```
### Checkbox
Refactored to use compound components. The single `` is now split into
explicit parts for full control over structure and styling.
**Prop Changes:**
- `isChecked` → `checked`
- `isDisabled` → `disabled`
- `isInvalid` → `invalid`
- `isReadOnly` → `readOnly`
- `isIndeterminate` → `checked="indeterminate"`
- `onChange` → `onCheckedChange`
- `colorScheme` → `colorPalette`
- `icon` → render as children of `Checkbox.Control`
- `iconColor` → `color` on `Checkbox.Indicator`
- `iconSize` → `boxSize` on `Checkbox.Indicator`
- `isFocusable` → removed
**CheckboxGroup:**
- `isDisabled` → `disabled`
- `onChange` → `onValueChange`
- `isNative` → removed
Before:
```tsx
import { Checkbox } from "@chakra-ui/react"
const Demo = () => (
setChecked(e.target.checked)}
colorScheme="blue"
>
Accept terms
)
```
After:
```tsx
import { Checkbox } from "@chakra-ui/react"
const Demo = () => (
setChecked(!!e.checked)}
colorPalette="blue"
>
Accept terms
)
```
### Radio Group
Refactored to use compound components. `Radio` is now `RadioGroup.Item` with
explicit sub-components: `ItemHiddenInput`, `ItemIndicator`, `ItemText`.
**Component Renaming:**
- `RadioGroup` → `RadioGroup.Root`
- `Radio` → `RadioGroup.Item` (with required sub-components)
**RadioGroup Prop Changes:**
- `onChange` → `onValueChange` (receives `{ value }` object)
- `colorScheme` → `colorPalette`
**Radio Prop Changes:**
- `isDisabled` → `disabled`
- `isInvalid`, `isChecked`, `defaultChecked` → removed (controlled from Root)
- `colorScheme` → removed from items (set `colorPalette` on Root instead)
- `inputProps` → spread into `RadioGroup.ItemHiddenInput`
Before:
```tsx
import { Radio, RadioGroup } from "@chakra-ui/react"
const Demo = () => (
setValue(val)}>
Option 1
Option 2
)
```
After:
```tsx
import { RadioGroup } from "@chakra-ui/react"
const Demo = () => (
setValue(e.value)}>
Option 1
Option 2
)
```
### Button Props
- `isActive` → `data-active` attribute
- `isDisabled` → `disabled`
- `isLoading` → `loading`
- `leftIcon` and `rightIcon` → passed as children
- `iconSpacing` → removed (use gap in flex layout)
- `colorScheme` → `colorPalette`
**Example:**
```tsx
// Before
}
rightIcon={}
colorScheme="blue"
>
Submit
// After
```
### Input Props
- `isDisabled` → `disabled`
- `isInvalid` → `invalid`
- `isReadOnly` → `readOnly`
- `isRequired` → `required`
- `colorScheme` → `colorPalette`
- `focusBorderColor` → use CSS variables
- `errorBorderColor` → use CSS variables
**Example:**
```tsx
// Before
// After
```
### Checkbox Props
- `isChecked` → `checked`
- `isDisabled` → `disabled`
- `isInvalid` → `invalid`
- `isIndeterminate` → `checked="indeterminate"`
- `onChange` → `onCheckedChange`
- `colorScheme` → `colorPalette`
- `iconColor` → `color` on `Checkbox.Indicator`
- `iconSize` → `boxSize` on `Checkbox.Indicator`
- `isFocusable` → removed
### Modal to Dialog Props
- `isOpen` → `open`
- `onClose` → `onOpenChange` (receives `{ open }`)
- `isCentered` → `placement="center"`
- `closeOnOverlayClick` → `closeOnInteractOutside`
- `closeOnEsc` → `closeOnEscape`
- `blockScrollOnMount` → `preventScroll`
- `onOverlayClick` → `onInteractOutside`
- `onEsc` → `onEscapeKeyDown`
- `onCloseComplete` → `onExitComplete`
- `initialFocusRef` → `initialFocusEl` (function returning element)
- `finalFocusRef` → `finalFocusEl` (function returning element)
- `scrollBehavior` → unchanged
- `motionPreset` → unchanged
- `trapFocus` → unchanged
- Sizes `2xl`–`6xl` → mapped to `xl`
- Removed: `allowPinchZoom`, `lockFocusAcrossFrames`, `preserveScrollBarGap`,
`returnFocusOnClose`, `useInert`, `portalProps`
### Stack Props
- `spacing` → `gap`
- `divider` → `separator`
- Other props remain the same
**Example:**
```tsx
// Before
}
>
Item 1
Item 2
// After
}
>
Item 1
Item 2
```