Skip to Content
DocsEnterpriseBlogShowcase

Migration to v3

How to migrate to Chakra UI v3.x from v2.x

warning
This guide is a work in progress and will be updated from time to time. If you find any point we missed, feel free to open a PR to update this guide.

Steps

The minimum node version required is Node.20.x

1

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

Next, 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 add
2

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` },
      },
    },
  },
})

All token values need to be wrapped in an object with a value key. Learn more about tokens here.

3

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 4x and re-render performance by 1.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 typigns 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
  • 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

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 add wrap=wrap to it.
  • WrapItem: Replace with Flex and add align=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.

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 native colorScheme 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

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 to Avatar.Fallback component

Wrap

We changed the props for the Wrap component to be more explicit and easier to understand.

  • Changed spacing to gap
  • Changed spacingX to rowGap
  • Changed spacingY to columnGap
  • Removed shouldWrapChildren in favor of using the WrapItem component explicitly

Portal

  • Remove appendToParentPortal prop in favor of using the containerRef
  • Remove PortalManager component

Stack

  • Changed spacing to gap
  • Removed StackItem in favor of using the Box component directly

Collapse

  • Rename Collapse to Collapsible namespace
  • Rename in to open
  • animateOpacity has been removed, use keyframes animations expand-height and collapse-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 the Image component directly

PinInput

  • Changed value, defaultValue and onChange to use string[] instead of string
  • Add new PinInput.Control and PinInput.Label component parts
  • PinInput.Root now renders a div element by default. Consider combining with Stack or Group for better layout control

NumberInput

  • Rename NumberInputStepper to NumberInput.Control
  • Rename NumberInputStepperIncrement to NumberInput.IncrementTrigger
  • Rename NumberInputStepperDecrement to NumberInput.DecrementTrigger
  • Remove focusBorderColor and errorBorderColor, 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 and borderInlineStartWidth
  • 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 a Field 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>
  • Removed external prop in favor of explicitly setting the target and rel props

Before:

<Link external>Click me</Link>

After:

<Link target="_blank" rel="noopener noreferrer">
  Click me
</Link>