Migration to v3
How to migrate to Chakra UI v3.x from v2.x
Steps
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-motion
Install updated versions of the packages: @chakra-ui/react
and
@emotion/react
.
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.
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
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` },
},
},
},
})
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>
)
The Provider component compose the ChakraProvider
from Chakra and
ThemeProvider
from next-themes
Improvements
-
Performance: Improved reconciliation performance by
4x
and 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
as
prop typings in favor of using theasChild
prop. This pattern was inspired by Radix Primitives and Ark UI.
Removed Features
Color Mode
ColorModeProvider
anduseColorMode
have been removed in favor ofnext-themes
LightMode
,DarkMode
andColorModeScript
components have been removed. You now have to useclassName="light"
orclassName="dark"
to force themes.useColorModeValue
has been removed in favor ofuseTheme
fromnext-themes
next-themes
Hooks
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 preview
Removed Components
- Wrap: Replace with
HStack
and addwrap=wrap
to it. - WrapItem: Replace with
Flex
and addalign=flex-start
to it. - StackItem: You don't need this anymore. Use
Box
instead. - FocusLock: We no longer ship a focus lock component. Use
react-focus-lock
instead. - FormControl and FormErrorMessage: Replace with the
Field
component
Prop Changes
Boolean Props
Changed naming convention for boolean properties from is<X>
to <x>
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 nativecolorScheme
prop in HTML elements
<Button colorScheme="blue">Click me</Button>
After
- You can now use
colorPalette
anywhere
<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
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
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
->lineClamp
truncated
->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
ChakraProvider
-
Removed
theme
prop in favor of passing thesystem
prop instead. Import thedefaultSystem
module instead oftheme
-
Removed
resetCss
prop in favor of passingpreflight: false
to thecreateSystem
function
Before
<ChakraProvider resetCss={false}>
<Component />
</ChakraProvider>
After
const system = createSystem(defaultConfig, { preflight: false })
<Provider value={system}>
<Component />
</Provider>
- Removed support for configuring toast options. Pass it to the
createToaster
function incomponents/ui/toaster.tsx
file instead.
Modal
- Renamed to
Dialog
- Remove
isCentered
prop in favor of using theplacement=center
prop - Removed
isOpen
andonClose
props in favor of using theopen
andonOpenChange
props
Avatar
- Remove
max
prop in favor of userland control - Remove excess label part
- Move image related props to
Avatar.Image
component - Move fallback icon to
Avatar.Fallback
component - Move
name
prop toAvatar.Fallback
component
Portal
- Remove
appendToParentPortal
prop in favor of using thecontainerRef
- Remove
PortalManager
component
Stack
- Changed
spacing
togap
- Removed
StackItem
in favor of using theBox
component directly
Collapse
- Rename
Collapse
toCollapsible
namespace - Rename
in
toopen
animateOpacity
has been removed, use keyframes animationsexpand-height
andcollapse-height
instead
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
img
without any fallback - Remove
fallbackSrc
due to the SSR issues it causes - Remove
useImage
hook - Remove
Img
in favor of using theImage
component directly
PinInput
- Changed
value
,defaultValue
andonChange
to usestring[]
instead ofstring
- Add new
PinInput.Control
andPinInput.Label
component parts PinInput.Root
now renders adiv
element by default. Consider combining withStack
orGroup
for better layout control
NumberInput
- Rename
NumberInputStepper
toNumberInput.Control
- Rename
NumberInputStepperIncrement
toNumberInput.IncrementTrigger
- Rename
NumberInputStepperDecrement
toNumberInput.DecrementTrigger
- Remove
focusBorderColor
anderrorBorderColor
, consider setting the--focus-color
and--error-color
css 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
div
element for better layout control - Simplify component to rely on
borderTopWidth
andborderInlineStartWidth
- To change the thickness reliably, set the
--divider-border-width
css variable
Input, Select, Textarea
- Removed
invalid
prop in favor of wrapping the component in aField
component. 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
isExternal
prop in favor of explicitly setting thetarget
andrel
props
Before
<Link isExternal>Click me</Link>
After
<Link target="_blank" rel="noopener noreferrer">
Click me
</Link>
IconButton
- Removed
icon
prop in favor of rendering thechildren
prop directly - Removed
isRounded
in favor of using theborderRadius=full
prop