Migration to v3
How to migrate to Chakra UI v3.x from v2.x
Steps
The minimum node version required is Node.20.x
Update Packages
Remove the unused packages: @emotion/styled and framer-motion. These
packages are no longer required in Chakra UI.
npm uninstall @emotion/styled framer-motionNext, install component snippets using the CLI snippets. Snippets that provide pre-built compositions of Chakra components to save you time and put you in charge.
npx @chakra-ui/cli@next snippet addRefactor Custom Theme
Move your custom theme to a dedicated theme.js or theme.ts file. Use
createSystem and defaultConfig to configure your theme.
Before
import { extendTheme } from "@chakra-ui/react"
export const theme = extendTheme({
fonts: {
heading: `'Figtree', sans-serif`,
body: `'Figtree', sans-serif`,
},
})After
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.
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
import { ChakraProvider } from "@chakra-ui/react"
export const App = ({ Component }) => (
<ChakraProvider theme={theme}>
<Component />
</ChakraProvider>
)After
import { Provider } from "@/components/ui/provider"
import { defaultSystem } from "@chakra-ui/react"
export const App = ({ Component }) => (
<Provider value={defaultSystem}>
<Component />
</Provider>
)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
4xand re-render performance by1.6x -
Namespaced imports: Import components using the dot notation for more concise imports
import { Accordion } from "@chakra-ui/react" const Demo = () => { return ( <Accordion.Root> <Accordion.Item> <Accordion.ItemTrigger /> <Accordion.ItemContent /> </Accordion.Item> </Accordion.Root> ) } -
TypeScript: Improved IntelliSense and type inference for style props and tokens.
-
Polymorphism: Loosened the
asprop typigns in favor of using theasChildprop. This pattern was inspired by Radix Primitives and Ark UI.
Removed Features
Color Mode
ColorModeProvideranduseColorModehave been removed in favor ofnext-themesLightMode,DarkModeandColorModeScriptcomponents have been removeduseColorModeValuehas been removed in favor ofuseThemefromnext-themes
next-themesHooks
We removed the hooks package in favor of using dedicated, robust libraries like
react-use and usehooks-ts
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.
<Box asChild>
<NextImage />
</Box>To style the Next.js link component, use the asChild prop on the
<Link isExternal asChild>
<NextLink />
</Link>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
defineStyle({
bg: transparentize("blue.200", 0.16)(theme),
// -> rgba(0, 0, 255, 0.16)
})After
We now use CSS color-mix
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.
Icons
Removed @chakra-ui/icons package. Prefer to use lucide-react or
react-icons instead.
Storybook Addon
We're removed the storybook addon in favor of using @storybook/addon-themes
and withThemeByClassName helper.
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<ReactRenderer>({
defaultTheme: "light",
themes: {
light: "",
dark: "dark",
},
}),
(Story) => (
<ChakraProvider value={defaultSystem}>
<Story />
</ChakraProvider>
),
],
}
export default previewRemoved Components
- Wrap: Replace with
HStackand addwrap=wrapto it. - WrapItem: Replace with
Flexand addalign=flex-startto it. - StackItem: You don't need this anymore. Use
Boxinstead. - FocusLock: We no longer ship a focus lock component. Use
react-focus-lockinstead.
Prop Changes
Boolean Props
Changed naming convention for boolean properties from is<X> to <x>
isOpen->opendefaultIsOpen->defaultOpenisDisabled->disabledisInvalid->invalidisRequired->required
ColorScheme Prop
The colorScheme prop has been changed to colorPalette
Before
- You could only use
colorSchemein a component's theme colorSchemeclashes with the nativecolorSchemeprop in HTML elements
<Button colorScheme="blue">Click me</Button>After
- You can now use
colorPaletteanywhere
<Button colorPalette="blue">Click me</Button>Usage in any component, you can do something like:
<Box colorPalette="red">
<Box bg="colorPalette.400">Some box</Box>
<Text color="colorPalette.600">Some text</Text>
</Box>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
<Box bgGradient="linear(to-r, red.200, pink.500)" />After
<Box bgGradient="to-r" gradientFrom="red.200" gradientTo="pink.500" />Color Palette
-
Default color palette is now
grayfor 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
const colors = { // ... gray: { 50: "#F7FAFC", 100: "#EDF2F7", 200: "#E2E8F0", 300: "#CBD5E0", 400: "#A0AEC0", 500: "#718096", 600: "#4A5568", 700: "#2D3748", 800: "#1A202C", 900: "#171923", }, }After
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->lineClamptruncated->truncate_activeLink->_currentPage_activeStep->_currentStep_mediaDark->_osDark_mediaLight->_osLight
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.
<Box
sx={{
svg: { color: "red.500" },
}}
/>After
Write nested styles using the css prop. All nested selectors require the
use of the ampersand & prefix
<Box
css={{
"& svg": { color: "red.500" },
}}
/>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
Avatar
- Remove
maxprop in favor of userland control - Remove excess label part
- Move image related props to
Avatar.Imagecomponent - Move fallback icon to
Avatar.Fallbackcomponent - Move
nameprop toAvatar.Fallbackcomponent
Wrap
We changed the props for the Wrap component to be more explicit and easier to
understand.
- Changed
spacingtogap - Changed
spacingXtorowGap - Changed
spacingYtocolumnGap - Removed
shouldWrapChildrenin favor of using theWrapItemcomponent explicitly
Portal
- Remove
appendToParentPortalprop in favor of using thecontainerRef - Remove
PortalManagercomponent
Stack
- Changed
spacingtogap - Removed
StackItemin favor of using theBoxcomponent directly
Collapse
- Rename
CollapsetoCollapsiblenamespace - Rename
intoopen animateOpacityhas been removed, use keyframes animationsexpand-heightandcollapse-heightinstead
Before:
<Collapse in={isOpen} animateOpacity>
Some content
</Collapse>After:
<Collapsible.Root open={isOpen}>
<Collapsible.Content>Some content</Collapsible.Content>
</Collapsible.Root>Image
- Now renders a native
imgwithout any fallback - Remove
fallbackSrcdue to the SSR issues it causes - Remove
useImagehook - Remove
Imgin favor of using theImagecomponent directly
PinInput
- Changed
value,defaultValueandonChangeto usestring[]instead ofstring - Add new
PinInput.ControlandPinInput.Labelcomponent parts PinInput.Rootnow renders adivelement by default. Consider combining withStackorGroupfor better layout control
NumberInput
- Rename
NumberInputSteppertoNumberInput.Control - Rename
NumberInputStepperIncrementtoNumberInput.IncrementTrigger - Rename
NumberInputStepperDecrementtoNumberInput.DecrementTrigger - Remove
focusBorderColoranderrorBorderColor, consider setting the--focus-colorand--error-colorcss variables instead
Before:
<NumberInput>
<NumberInputField />
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>After:
<NumberInput.Root>
<NumberInput.Field />
<NumberInput.Control>
<NumberInput.IncrementTrigger />
<NumberInput.DecrementTrigger />
</NumberInput.Control>
</NumberInput.Root>Divider
- Rename to
Separator - Switch to
divelement for better layout control - Simplify component to rely on
borderTopWidthandborderInlineStartWidth - To change the thickness reliably, set the
--divider-border-widthcss variable
Input, Select, Textarea
- Removed
invalidprop in favor of wrapping the component in aFieldcomponent. This allows for adding a label, error text and asterisk easily.
Before:
<Input invalid />After:
<Field invalid label="Email" errorText="This field is required">
<Input />
</Field>Link
- Removed
externalprop in favor of explicitly setting thetargetandrelprops
Before:
<Link external>Click me</Link>After:
<Link target="_blank" rel="noopener noreferrer">
Click me
</Link>