开始使用

从 v2 迁移

如何将 Chakra UI 从 v2.x 迁移到 v3.x。

AI 提示
想要跳过文档?可以使用我们的 Agent Skills

观看迁移直播(1)

前往观看

观看迁移直播(2)

前往观看
警告
我们建议使用 LLMs.txt 文件,让大语言模型能够读取 Chakra UI v3 文档。

Codemod(推荐)

codemod 可以自动化从 Chakra UI v2 迁移到 v3 的过程。它会处理组件重命名、属性变更、导入更新以及复合组件重构。 在手动迁移之前,请从这里开始。

bash
npx @chakra-ui/codemod upgrade

使用 --dry 参数可以先预览变更,而不会修改你的文件。

手动迁移步骤

提示
所需的最低 Node.js 版本为 Node.20.x
1

更新依赖包

移除不再使用的依赖包:@emotion/styled framer-motion。这两个包在 Chakra UI 中不再需要。

bash
npm uninstall @emotion/styled framer-motion

安装更新的依赖包:@chakra-ui/react @emotion/react

bash
npm install @chakra-ui/react@latest @emotion/react@latest

接下来,使用 CLI 安装组件代码片段(snippets)。代码片段提供了 Chakra 组件的预构建组合,可以帮你节省时间,并让你把主动权掌握在自己手中。

bash
npx @chakra-ui/cli snippet add
2

重构自定义主题

把自定义主题移动到一个专门的 theme.js theme.ts 文件中。使用 createSystemdefaultConfig 来配置你的主题。

Before(迁移前)

tsx
import { extendTheme } from "@chakra-ui/react"

export const theme = extendTheme({
  fonts: {
    heading: `'Figtree', sans-serif`,
    body: `'Figtree', sans-serif`,
  },
})

After(迁移后)

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

所有 token 值都需要包裹在一个带有 value 键的对象中。此处可了解更多 token 相关知识: tokens

3

更新 ChakraProvider

将 ChakraProvider 的导入从 @chakra-ui/react 改为使用代码片段提供的那个。接着把 theme 属性重命名为 value,以匹配新的基于 system 的主题方式。

Before(迁移前)

tsx
import { ChakraProvider } from "@chakra-ui/react"

export const App = ({ Component }) => (
  <ChakraProvider theme={theme}>
    <Component />
  </ChakraProvider>
)

After(迁移后)

tsx
import { Provider } from "@/components/ui/provider"
import { defaultSystem } from "@chakra-ui/react"

export const App = ({ Component }) => (
  <Provider>
    <Component />
  </Provider>
)
tsx
import { ColorModeProvider } from "@/components/ui/color-mode"
import { ChakraProvider, defaultSystem } from "@chakra-ui/react"

export function Provider(props) {
  return (
    <ChakraProvider value={defaultSystem}>
      <ColorModeProvider {...props} />
    </ChakraProvider>
  )
}

如果你有自定义主题,请把 defaultSystem 替换为你的自定义 system

Provider 组件组合了 Chakra 的 ChakraProvider next-themesThemeProvider

核心改进

  • 性能(Performance): 协调(reconciliation)性能提升 4 倍,重渲染性能提升 1.6 倍
  • 命名空间导入(Namespaced imports): 使用点记法导入组件,让导入更简洁
    tsx
    import { Accordion } from "@chakra-ui/react"
    
    const Demo = () => {
      return (
        <Accordion.Root>
          <Accordion.Item>
            <Accordion.ItemTrigger />
            <Accordion.ItemContent />
          </Accordion.Item>
        </Accordion.Root>
      )
    }
  • TypeScript: 改善了样式属性和 token 的 IntelliSense 与类型推断。
  • 多态(Polymorphism): 放宽了 as 属性的类型定义,转而推荐使用 asChild 属性。此模式受到了 Radix Primitives 与 Ark UI 的启发。

已移除的功能

颜色模式(Color Mode)

  • ColorModeProvider useColorMode 已被移除,改用 next-themes

  • LightModeDarkMode ColorModeScript 组件已被移除。现在你需要使用 className="light"className="dark" 来强制主题。

  • useColorModeValue 已被移除,改用 next-themesuseTheme

提示
我们通过 CLI 提供了颜色模式的代码片段,帮助你使用 next-themes 快速搭建颜色模式。

Hooks

我们移除了 hooks 包,转而使用更专注、更健壮的库,例如 react-useusehooks-ts

我们现在唯一提供的 hooks 是 useBreakpointValue useCallbackRefuseDisclosure useControllableStateuseMediaQuery

样式配置(Style Config)

我们移除了 styleConfig multiStyleConfig 概念,改用 recipe 与 slot recipe。此模式借鉴了 Panda CSS。

Next.js 包

我们移除了 @chakra-ui/next-js 包,改用 asChild 属性以获得更好的灵活性。

要样式化 Next.js 的 image 组件,请在 Box 组件上使用 asChild 属性。

tsx
<Box asChild>
  <NextImage />
</Box>

要样式化 Next.js 的 link 组件,请在 Link 组件上使用 asChild 属性。

tsx
<Link isExternal asChild>
  <NextLink />
</Link>

主题工具(Theme Tools)

我们移除了这个包,改用 CSS 颜色混合(color-mix)。

Before(迁移前)

我们之前使用 JS 来计算颜色,然后再应用透明度:

tsx
defineStyle({
  bg: transparentize("blue.200", 0.16)(theme),
  // -> rgba(0, 0, 255, 0.16)
})

After(迁移后)

现在我们使用 CSS 的 color-mix:

tsx
defineStyle({
  bg: "blue.200/16",
  // -> color-mix(in srgb, var(--chakra-colors-blue-200), transparent 16%)
})

forwardRef

由于 as 属性被简化,我们不再提供自定义的 forwardRef。建议直接使用 React 中的 forwardRef

Before(迁移前)

tsx
import { Button as ChakraButton, forwardRef } from "@chakra-ui/react"

const Button = forwardRef<ButtonProps, "button">(function Button(props, ref) {
  return <ChakraButton ref={ref} {...props} />
})

After(迁移后)

tsx
import { Button as ChakraButton } from "@chakra-ui/react"
import { forwardRef } from "react"

const Button = forwardRef<HTMLButtonElement, ButtonProps>(
  function Button(props, ref) {
    return <ChakraButton ref={ref} {...props} />
  },
)

图标(Icons)

移除了 @chakra-ui/icons 包。请使用 react-icons(推荐 Lucide 图标)或 lucide-react。使用 npm install react-icons 进行安装。

  • 不带 props 的图标 → 直接使用 react-icon

  • 带 Chakra 样式属性的图标 → 用 @chakra-ui/react Icon 包裹

Before(迁移前)

tsx
import { AddIcon, CheckIcon } from "@chakra-ui/icons"

<AddIcon />
<CheckIcon boxSize={6} color="green.500" />

After(迁移后)

tsx
import { Icon } from "@chakra-ui/react"
import { LuCheck, LuPlus } from "react-icons/lu"

<LuPlus />
<Icon as={LuCheck} boxSize={6} color="green.500" />

常见图标映射:

  • 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

自定义 SVG 图标

在 v2 中,<Icon> 会渲染一个 <svg> 包裹,并把 SVG 子元素(例如 <path>)直接传入。在 v3 中,使用自定义 SVG 时,请使用 asChild 属性,使 Icon 把你的样式合并到 <svg> 元素上。

Before(迁移前)

tsx
import { Icon } from "@chakra-ui/react"

<Icon viewBox="0 0 24 24" color="red.500" boxSize={6}>
  <path d="M12 2L2 22h20L12 2z" fill="currentColor" />
</Icon>

After(迁移后)

tsx
import { Icon } from "@chakra-ui/react"

<Icon color="red.500" size="md" asChild>
  <svg viewBox="0 0 24 24">
    <path d="M12 2L2 22h20L12 2z" fill="currentColor" />
  </svg>
</Icon>

另外,也可以使用 createIcon 定义可复用的自定义图标:

tsx
import { createIcon } from "@chakra-ui/react"

const TriangleIcon = createIcon({
  displayName: "TriangleIcon",
  viewBox: "0 0 24 24",
  path: <path d="M12 2L2 22h20L12 2z" fill="currentColor" />,
})

// Usage
<TriangleIcon size="lg" color="red.500" />

Storybook Addon

我们移除了 storybook addon,改用 @storybook/addon-themes withThemeByClassName 辅助函数。

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<ReactRenderer>({
      defaultTheme: "light",
      themes: {
        light: "",
        dark: "dark",
      },
    }),
    (Story) => (
      <ChakraProvider value={defaultSystem}>
        <Story />
      </ChakraProvider>
    ),
  ],
}

export default preview

已移除的组件

  • StackItem:你不再需要它了。请改用 Box

  • FocusLock:我们不再自带焦点锁定组件。请直接安装并使用 react-focus-lock

  • AlertDialog:改用 Dialog 组件并设置 role="alertdialog";将 leastDestructiveRef 属性设置为 Dialog.Root 组件的 initialFocusEl

CircularProgress

  • 已改名为 ProgressCircle,并使用复合组件

  • isIndeterminate 变为 value={null}

  • thickness 属性变为 --thickness CSS 变量

  • color 属性变为 ProgressCircle.Range 上的 stroke 属性

Before(迁移前)

tsx
<CircularProgress
  value={75}
  thickness="4px"
  color="blue.500"
  isIndeterminate={false}
/>

After(迁移后)

tsx
<ProgressCircle.Root value={75}>
  <ProgressCircle.Circle css={{ "--thickness": "4px" }}>
    <ProgressCircle.Track />
    <ProgressCircle.Range stroke="blue.500" />
  </ProgressCircle.Circle>
</ProgressCircle.Root>

对于不定进度条(indeterminate),使用:

tsx
<ProgressCircle.Root value={null}>
  <ProgressCircle.Circle>
    <ProgressCircle.Track />
    <ProgressCircle.Range />
  </ProgressCircle.Circle>
</ProgressCircle.Root>

StackDivider

  • 不再作为独立组件提供

  • 在 stack 项之间使用显式的 Separator 组件

Before(迁移前)

tsx
<VStack divider={<StackDivider borderColor="gray.200" />} spacing={4}>
  <Box>Item 1</Box>
  <Box>Item 2</Box>
  <Box>Item 3</Box>
</VStack>

After(迁移后)

tsx
import { Separator, VStack, Box } from "@chakra-ui/react"

<VStack gap={4}>
  <Box>Item 1</Box>
  <Separator borderColor="gray.200" />
  <Box>Item 2</Box>
  <Separator borderColor="gray.200" />
  <Box>Item 3</Box>
</VStack>

属性变更

布尔属性(Boolean Props)

布尔属性的命名约定已从 is<X> 改为 <x>

  • isOpen → open
  • defaultIsOpen → defaultOpen
  • isDisabled → disabled
  • isInvalid → invalid
  • isRequired → required

ColorScheme 属性

colorScheme 属性已改名为 colorPalette

Before(迁移前)

  • 你只能在组件的主题中使用 colorScheme

  • colorScheme 与 HTML 原生元素上的 colorScheme 属性相冲突

tsx
<Button colorScheme="blue">Click me</Button>

After(迁移后)

  • 现在你可以在任何地方使用 colorPalette

tsx
<Button colorPalette="blue">Click me</Button>

在任何组件中,你都可以这样使用:

tsx
<Box colorPalette="red">
  <Box bg="colorPalette.400">Some box</Box>
  <Text color="colorPalette.600">Some text</Text>
</Box>

如果使用自定义颜色,必须定义两样东西才能让 colorPalette 生效:

  • tokens:用于 50-950 的配色盘

  • semanticTokens:用于 solid contrastfg mutedsubtle emphasizedfocusRing 颜色键

theme.ts

tsx
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}" },
        },
      },
    },
  },
})

在此可了解更多: 自定义颜色指南

渐变属性(Gradient Props)

渐变相关的样式属性简化为 gradient gradientFromgradientTo 三个属性。这降低了解析渐变字符串的运行时性能开销,并带来更好的类型推断。

Before(迁移前)

tsx
<Box bgGradient="linear(to-r, red.200, pink.500)" />

After(迁移后)

tsx
<Box bgGradient="to-r" gradientFrom="red.200" gradientTo="pink.500" />

配色盘(Color Palette)

  • 现在所有组件的默认配色盘都是 gray,但你可以通过主题配置它。

  • 默认主题的配色盘大小已增加到 11 个色阶(shades),以支持更多颜色变化。

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)

一些样式属性的命名约定已更改:

  • noOfLines → lineClamp
  • truncated → truncate
  • _activeLink → _currentPage
  • _activeStep → _currentStep
  • _mediaDark → _osDark
  • _mediaLight → _osLight

示例:

tsx
// Before
<Text noOfLines={2}>
  Long text that will be clamped to 2 lines
</Text>

<Text truncated>
  This text will be truncated with ellipsis
</Text>

// After
<Text lineClamp={2}>
  Long text that will be clamped to 2 lines
</Text>

<Text truncate>
  This text will be truncated with ellipsis
</Text>

我们移除了 apply 属性,改用 textStylelayerStyles

嵌套样式(Nested Styles)

我们改变了在 Chakra UI 组件中编写嵌套样式的方式。

Before(迁移前)

tsx
<Box
  sx={{
    svg: { color: "red.500" },
  }}
/>

After(迁移后)

tsx
<Box
  css={{
    "& svg": { color: "red.500" },
  }}
/>

这样做有两个原因:

  • 更快的样式处理: 之前我们必须检查样式键到底是样式属性还是选择器, 整体开销相当大。

  • 更好的类型提示: 这使得嵌套样式属性更容易被强类型化。

组件变更

ChakraProvider

移除了 theme 属性,改为传入 System 属性。导入 defaultSystem 模块,而不再导入 theme

移除了 resetCss 属性,改为向 createSystem 函数传入 preflight: false

不再支持配置 toast 选项,请改在 components/ui/toaster.tsx 文件中的 createToaster 函数里配置。

Before(迁移前)

tsx
<ChakraProvider resetCss={false}>
  <Component />
</ChakraProvider>

After(迁移后)

tsx
const system = createSystem(defaultConfig, { preflight: false })

<Provider value={system}>
  <Component />
</Provider>

Modal

已改名为 Dialog,并使用复合组件,带有显式的 Dialog.PositionerPortal 包裹。

组件重命名:

  • Modal → Dialog.Root
  • ModalOverlay → Dialog.Backdrop
  • ModalContent → Dialog.Content(包裹于 Dialog.Positioner 中)
  • ModalHeader → Dialog.Header
  • ModalBody → Dialog.Body
  • ModalFooter → Dialog.Footer
  • ModalCloseButton → Dialog.CloseTrigger

属性变更:

  • isOpen → open
  • onClose → onOpenChange(接收 { open }
  • isCentered → placement="center"
  • closeOnOverlayClick → closeOnInteractOutside
  • closeOnEsc → closeOnEscape
  • blockScrollOnMount → preventScroll
  • onOverlayClick → onInteractOutside
  • onEsc → onEscapeKeyDown
  • onCloseComplete → onExitComplete
  • initialFocusRef → initialFocusEl={() => ref.current}
  • finalFocusRef → finalFocusEl={() => ref.current}

尺寸映射: v3 中 2xl 6xl 的尺寸都会被映射为 xl

已移除的属性: allowPinchZoomlockFocusAcrossFrames preserveScrollBarGap returnFocusOnCloseuseInert portalProps

Avatar

现在使用声明式组合模式,包含独立的 Avatar.Image Avatar.Fallback 部件。

组件重命名:

  • Avatar → Avatar.Root
  • AvatarBadge → 已移除(改用 Float + Circle)
  • AvatarGroup → AvatarGroup(保持不变,但移除了 max 属性)

移入 Avatar.Image 的属性:

  • src、srcSet、sizes、loading、referrerPolicy、crossOrigin

移入 Avatar.Fallback 的属性:

  • name — 自动生成首字母缩写
  • icon — 改为以 children 方式渲染
  • iconLabel → aria-label

已移除的属性:

  • ignoreFallback — 不再需要
  • showBorder — 改用 border 和 borderColor 样式属性
  • AvatarGroup 的 max — 已移除,请在业务代码中处理
  • AvatarGroup 的 spacing → spaceX

Before(迁移前)

tsx
import { Avatar, AvatarBadge, AvatarGroup } from "@chakra-ui/react"

const Demo = () => (
  <>
    <Avatar name="Dan Abrahmov" src="https://bit.ly/dan-abramov" size="md" />

    <Avatar bg="red.500" icon={<AiOutlineUser />} />

    <Avatar>
      <AvatarBadge boxSize="1.25em" bg="green.500" />
    </Avatar>
  </>
)

After(迁移后)

tsx
import { Avatar, AvatarGroup, Circle, Float } from "@chakra-ui/react"

const Demo = () => (
  <>
    <Avatar.Root size="md">
      <Avatar.Fallback name="Dan Abrahmov" />
      <Avatar.Image src="https://bit.ly/dan-abramov" />
    </Avatar.Root>

    <Avatar.Root bg="red.500">
      <Avatar.Fallback>
        <AiOutlineUser />
      </Avatar.Fallback>
    </Avatar.Root>

    <Avatar.Root>
      <Avatar.Image src="https://bit.ly/dan-abramov" />
      <Float placement="bottom-end" offsetX="1" offsetY="1">
        <Circle
          bg="green.500"
          size="8px"
          outline="0.2em solid"
          outlineColor="bg"
        />
      </Float>
    </Avatar.Root>
  </>
)

Breadcrumb

现在使用复合组件,在条目之间使用显式分隔符,并需要一个必填的 Breadcrumb.List 包裹。

组件重命名:

  • Breadcrumb → Breadcrumb.Root
  • BreadcrumbItem → Breadcrumb.Item
  • BreadcrumbLink → Breadcrumb.Link
  • 带 isCurrentPage 的 BreadcrumbLink → Breadcrumb.CurrentLink
  • BreadcrumbSeparator → Breadcrumb.Separator

属性变更:

  • separator → 已移除,改在条目之间使用显式分隔符组件
  • spacing → gap(移入 Breadcrumb.List)
  • BreadcrumbItem 上的 isCurrentPage → 改用 Breadcrumb.CurrentLink
  • isLastChild → 已移除(使用显式分隔符后不再需要)
  • listProps → 直接展开到 Breadcrumb.List 上

Before(迁移前)

tsx
import { Breadcrumb, BreadcrumbItem, BreadcrumbLink } from "@chakra-ui/react"

const Demo = () => (
  <Breadcrumb separator="-" spacing="8px">
    <BreadcrumbItem>
      <BreadcrumbLink href="#">Home</BreadcrumbLink>
    </BreadcrumbItem>
    <BreadcrumbItem isCurrentPage>
      <BreadcrumbLink href="#">Current</BreadcrumbLink>
    </BreadcrumbItem>
  </Breadcrumb>
)

After(迁移后)

tsx
import { Breadcrumb } from "@chakra-ui/react"

const Demo = () => (
  <Breadcrumb.Root>
    <Breadcrumb.List gap="8px">
      <Breadcrumb.Item>
        <Breadcrumb.Link href="#">Home</Breadcrumb.Link>
      </Breadcrumb.Item>
      <Breadcrumb.Separator>-</Breadcrumb.Separator>
      <Breadcrumb.Item>
        <Breadcrumb.CurrentLink>Current</Breadcrumb.CurrentLink>
      </Breadcrumb.Item>
    </Breadcrumb.List>
  </Breadcrumb.Root>
)

Portal

  • 移除了 appendToParentPortal 属性,改用 containerRef

  • 移除了 PortalManager 组件

Progress

  • 现在使用 Progress.RootProgress.Track Progress.Range 复合组件

  • hasStripe 属性改名为 striped

  • isAnimated 属性改名为 animated

  • colorScheme 属性改名为 colorPalette

Before(迁移前)

tsx
<Progress hasStripe isAnimated value={75} colorScheme="blue" />

After(迁移后)

tsx
<Progress.Root striped animated value={75} colorPalette="blue">
  <Progress.Track>
    <Progress.Range />
  </Progress.Track>
</Progress.Root>

Stack

  • spacing 改为 gap

  • 移除了 StackItem,请直接使用 Box 组件

  • divider 改为 separator

Select

现在叫 NativeSelect,并暴露了所有部件。

Before(迁移前)

tsx
<Select placeholder="Select option">
  <option value="option1">Option 1</option>
  <option value="option2">Option 2</option>
  <option value="option3">Option 3</option>
</Select>

After(迁移后)

tsx
<NativeSelect.Root size="sm" width="240px">
  <NativeSelect.Field placeholder="Select option">
    <option value="option1">Option 1</option>
    <option value="option2">Option 2</option>
    <option value="option3">Option 3</option>
  </NativeSelect.Field>
  <NativeSelect.Indicator />
</NativeSelect.Root>

更换图标:

Before(迁移前)

tsx
<Select icon={<MdArrowDropDown />} placeholder="Woohoo! A new icon" />

After(迁移后)

tsx
<NativeSelect.Indicator>
  <MdArrowDropDown />
</NativeSelect.Indicator>

Collapse

  • Collapse 改名为 Collapsible 命名空间

  • in 改名为 open

  • animateOpacity 已被移除,需改用 expand-heightcollapse-height 关键帧动画

Before(迁移前)

tsx
<Collapse in={isOpen} animateOpacity>
  Some content
</Collapse>

After(迁移后)

tsx
<Collapsible.Root open={isOpen}>
  <Collapsible.Content>Some content</Collapsible.Content>
</Collapsible.Root>

Image

现在渲染原生的 img,不内置回退(fallback)逻辑。 Img 已并入 Image

  • Img → Image
  • fit → objectFit
  • align → objectPosition
  • fallbackSrc、fallback、ignoreFallback、fallbackStrategy → 已移除
  • useImage hook → 已移除

Before(迁移前)

tsx
import { Img } from "@chakra-ui/react"

const Demo = () => (
  <Img
    src="photo.jpg"
    fit="cover"
    align="center"
    fallbackSrc="placeholder.jpg"
  />
)

After(迁移后)

tsx
import { Image } from "@chakra-ui/react"

const Demo = () => (
  <Image src="photo.jpg" objectFit="cover" objectPosition="center" />
)

如需回退行为,请使用原生 onError 事件来切换 src

PinInput

现在使用复合组件。每个 input 都需要 index 属性,并且必须包裹在 PinInput.Control 中。

组件重命名:

  • PinInput → PinInput.Root
  • PinInputField → PinInput.Input(需要 index 属性)

属性变更:

  • value / defaultValue → 现在为 string[],不再是 string
  • onChange → onValueChange(接收 { value, valueAsString }
  • onComplete → onValueComplete(接收 { value, valueAsString }
  • isDisabled → disabled
  • isInvalid → invalid
  • manageFocus → 已移除

Before(迁移前)

tsx
import { PinInput, PinInputField } from "@chakra-ui/react"

const Demo = () => (
  <PinInput defaultValue="23" onChange={setValue} onComplete={handleComplete}>
    <PinInputField />
    <PinInputField />
    <PinInputField />
  </PinInput>
)

After(迁移后)

tsx
import { PinInput } from "@chakra-ui/react"

const Demo = () => (
  <PinInput.Root
    defaultValue={["2", "3"]}
    onValueChange={(e) => setValue(e.value)}
    onValueComplete={(e) => handleComplete(e.value)}
  >
    <PinInput.HiddenInput />
    <PinInput.Control>
      <PinInput.Input index={0} />
      <PinInput.Input index={1} />
      <PinInput.Input index={2} />
    </PinInput.Control>
  </PinInput.Root>
)

Popover

现在使用复合组件,内容包裹在显式的 Popover.Positioner 中。PopoverTrigger 现在需要 asChild

组件重命名:

  • Popover → Popover.Root
  • PopoverTrigger → Popover.Trigger(添加 asChild)
  • PopoverContent → Popover.Content(包裹于 Popover.Positioner 中)
  • PopoverHeader → Popover.Title
  • PopoverBody → Popover.Body
  • PopoverFooter → Popover.Footer
  • PopoverArrow → Popover.Arrow
  • PopoverCloseButton → Popover.CloseTrigger
  • PopoverAnchor → Popover.Anchor

属性变更:

  • isOpen → open
  • defaultIsOpen → defaultOpen
  • onClose / onOpen → onOpenChange(接收 { open }
  • closeOnBlur → closeOnInteractOutside
  • closeOnEsc → closeOnEscape
  • isLazy → lazyMount
  • lazyBehavior="unmount" → unmountOnExit
  • initialFocusRef → initialFocusEl={() => ref.current}
  • trigger="hover" → 改用 HoverCard 组件
  • 定位属性(placement、gutter、flip、offset、matchWidth、strategy)→ 合并到 positioning 对象
  • matchWidth → positioning.sameWidth

已移除的属性: computePositionOnMount returnFocusOnClosearrowShadowColor modifiers

Before(迁移前)

tsx
import {
  Popover,
  PopoverArrow,
  PopoverBody,
  PopoverCloseButton,
  PopoverContent,
  PopoverHeader,
  PopoverTrigger,
} from "@chakra-ui/react"

const Demo = () => (
  <Popover placement="bottom" closeOnBlur={false} isLazy>
    <PopoverTrigger>
      <Button>Trigger</Button>
    </PopoverTrigger>
    <PopoverContent>
      <PopoverArrow />
      <PopoverCloseButton />
      <PopoverHeader>Title</PopoverHeader>
      <PopoverBody>Content here</PopoverBody>
    </PopoverContent>
  </Popover>
)

After(迁移后)

tsx
import { Popover } from "@chakra-ui/react"

const Demo = () => (
  <Popover.Root
    positioning={{ placement: "bottom" }}
    closeOnInteractOutside={false}
    lazyMount
  >
    <Popover.Trigger asChild>
      <Button>Trigger</Button>
    </Popover.Trigger>
    <Popover.Positioner>
      <Popover.Content>
        <Popover.Arrow />
        <Popover.CloseTrigger />
        <Popover.Title>Title</Popover.Title>
        <Popover.Body>Content here</Popover.Body>
      </Popover.Content>
    </Popover.Positioner>
  </Popover.Root>
)

Hover 触发 → HoverCard

如果你使用过 trigger="hover",请迁移到 HoverCard 组件:

Before(迁移前)

tsx
<Popover trigger="hover" openDelay={500}>
  <PopoverTrigger>
    <Button>Hover me</Button>
  </PopoverTrigger>
  <PopoverContent>
    <PopoverBody>Tooltip-like content</PopoverBody>
  </PopoverContent>
</Popover>

After(迁移后)

tsx
import { HoverCard } from "@chakra-ui/react"

const Demo = () => (
  <HoverCard.Root openDelay={500}>
    <HoverCard.Trigger asChild>
      <Button>Hover me</Button>
    </HoverCard.Trigger>
    <HoverCard.Positioner>
      <HoverCard.Content>
        <HoverCard.Arrow />
        Content here
      </HoverCard.Content>
    </HoverCard.Positioner>
  </HoverCard.Root>
)

NumberInput

组件重命名:

  • NumberInput → NumberInput.Root
  • NumberInputField → NumberInput.Input
  • NumberInputStepper → NumberInput.Control
  • NumberIncrementStepper → NumberInput.IncrementTrigger
  • NumberDecrementStepper → NumberInput.DecrementTrigger

属性变更:

  • isDisabled → disabled
  • isInvalid → invalid
  • isReadOnly → readOnly
  • isRequired → required
  • onChange → onValueChange(接收 { value, valueAsNumber }
  • onInvalid → onValueInvalid
  • keepWithinRange → allowOverflow(取值取反)
  • focusBorderColor / errorBorderColor → 改用 --focus-color / --error-color CSS 变量
  • parse 和 format → 已移除,改用 formatOptions

Before(迁移前)

tsx
import {
  NumberDecrementStepper,
  NumberIncrementStepper,
  NumberInput,
  NumberInputField,
  NumberInputStepper,
} from "@chakra-ui/react"

const Demo = () => (
  <NumberInput
    isDisabled
    onChange={(valStr, valNum) => {}}
    keepWithinRange={false}
  >
    <NumberInputField />
    <NumberInputStepper>
      <NumberIncrementStepper />
      <NumberDecrementStepper />
    </NumberInputStepper>
  </NumberInput>
)

After(迁移后)

tsx
import { NumberInput } from "@chakra-ui/react"

const Demo = () => (
  <NumberInput.Root disabled onValueChange={(e) => {}} allowOverflow>
    <NumberInput.Input />
    <NumberInput.Control>
      <NumberInput.IncrementTrigger />
      <NumberInput.DecrementTrigger />
    </NumberInput.Control>
  </NumberInput.Root>
)

Divider

已改名为 Separator,以更好地对齐语义化 HTML 和 ARIA 标准。 该组件现在使用 div 元素,以获得更好的布局控制。

  • Divider → Separator
  • 依赖 borderTopWidth 和 borderInlineStartWidth 进行样式处理
  • 要改变粗细,请设置 --divider-border-width CSS 变量
  • 所有属性(orientation、variant、样式)保持不变

Before(迁移前)

tsx
import { Divider } from "@chakra-ui/react"

const Demo = () => (
  <>
    <Divider orientation="horizontal" />
    <Divider orientation="vertical" height="20px" />
  </>
)

After(迁移后)

tsx
import { Separator } from "@chakra-ui/react"

const Demo = () => (
  <>
    <Separator orientation="horizontal" />
    <Separator orientation="vertical" height="20px" />
  </>
)

Card

现在使用点记法的复合组件。迁移很简单——只有组件名称会改变,所有属性保持不变。

组件重命名:

  • Card → Card.Root
  • CardHeader → Card.Header
  • CardBody → Card.Body
  • CardFooter → Card.Footer

v3 还引入了 Card.Title Card.Description 作为新的语义组件,以获得更合理的结构。

Before(迁移前)

tsx
import {
  Button,
  Card,
  CardBody,
  CardFooter,
  CardHeader,
  Heading,
  Text,
} from "@chakra-ui/react"

const Demo = () => (
  <Card maxW="sm">
    <CardHeader>
      <Heading size="md">Living room Sofa</Heading>
    </CardHeader>
    <CardBody>
      <Text>This sofa is perfect for modern tropical spaces.</Text>
      <Text color="blue.600" fontSize="2xl">
        $450
      </Text>
    </CardBody>
    <CardFooter>
      <Button variant="solid" colorScheme="blue">
        Buy now
      </Button>
    </CardFooter>
  </Card>
)

After(迁移后)

tsx
import { Button, Card, Heading, Text } from "@chakra-ui/react"

const Demo = () => (
  <Card.Root maxW="sm">
    <Card.Header>
      <Heading size="md">Living room Sofa</Heading>
    </Card.Header>
    <Card.Body>
      <Text>This sofa is perfect for modern tropical spaces.</Text>
      <Text color="blue.600" fontSize="2xl">
        $450
      </Text>
    </Card.Body>
    <Card.Footer>
      <Button variant="solid" colorPalette="blue">
        Buy now
      </Button>
    </Card.Footer>
  </Card.Root>
)

Input、Select、Textarea

移除了 invalid 属性,改为将组件包裹在 Field 组件中。这样可以轻松添加标签、错误文本和星号。

Before(迁移前)

tsx
<Input invalid />

After(迁移后)

tsx
<Field.Root invalid>
  <Field.Label>Email</Field.Label>
  <Input />
  <Field.ErrorText>This field is required</Field.ErrorText>
</Field.Root>

Link

移除了 isExternal 属性,改为显式设置 targetrel 属性。

Before(迁移前)

tsx
<Link isExternal>Click me</Link>

After(迁移后)

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

List

现在使用点记法的复合组件。OrderedList UnorderedList 不再是独立的组件——请使用 List.Root 配合 as 属性。

组件重命名:

  • List → List.Root
  • OrderedList → List.Root as="ol"
  • UnorderedList → List.Root as="ul"
  • ListItem → List.Item
  • ListIcon → List.Indicator

属性变更:

  • spacing → gap
  • styleType → listStyleType
  • stylePosition → listStylePosition

无序列表:

Before(迁移前)

tsx
import { ListIcon, ListItem, UnorderedList } from "@chakra-ui/react"
import { MdCheckCircle } from "react-icons/md"

const Demo = () => (
  <UnorderedList spacing={3}>
    <ListItem>
      <ListIcon as={MdCheckCircle} color="green.500" />
      Lorem ipsum dolor sit amet
    </ListItem>
    <ListItem>
      <ListIcon as={MdCheckCircle} color="green.500" />
      Consectetur adipiscing elit
    </ListItem>
  </UnorderedList>
)

After(迁移后)

tsx
import { List } from "@chakra-ui/react"
import { MdCheckCircle } from "react-icons/md"

const Demo = () => (
  <List.Root as="ul" gap={3}>
    <List.Item>
      <List.Indicator as={MdCheckCircle} color="green.500" />
      Lorem ipsum dolor sit amet
    </List.Item>
    <List.Item>
      <List.Indicator as={MdCheckCircle} color="green.500" />
      Consectetur adipiscing elit
    </List.Item>
  </List.Root>
)

有序列表:

Before(迁移前)

tsx
import { ListItem, OrderedList } from "@chakra-ui/react"

const Demo = () => (
  <OrderedList styleType="lower-roman" stylePosition="inside">
    <ListItem>First item</ListItem>
    <ListItem>Second item</ListItem>
  </OrderedList>
)

After(迁移后)

tsx
import { List } from "@chakra-ui/react"

const Demo = () => (
  <List.Root as="ol" listStyleType="lower-roman" listStylePosition="inside">
    <List.Item>First item</List.Item>
    <List.Item>Second item</List.Item>
  </List.Root>
)

Button

属性变更:

  • isActive → data-active 属性
  • isDisabled → disabled
  • isLoading → loading
  • colorScheme → colorPalette
  • leftIcon / rightIcon → 图标直接作为 children 渲染
  • iconSpacing → gap
  • variant="unstyled" → unstyled 布尔属性
  • variant="link" → variant="plain"

Before(迁移前)

tsx
<Button colorScheme="blue" isLoading leftIcon={<Download />} iconSpacing={2}>
  Download
</Button>

After(迁移后)

tsx
<Button colorPalette="blue" loading gap={2}>
  <Download />
  Download
</Button>

ButtonGroup 变更:

  • isAttached → attached
  • isDisabled → 已移除(请为每个子元素设置 disabled)

IconButton

  • icon → 直接以 children 方式渲染
  • isRound → borderRadius="full"
  • isDisabled → disabled

Before(迁移前)

tsx
<IconButton icon={<SearchIcon />} isRounded isDisabled aria-label="Search" />

After(迁移后)

tsx
<IconButton borderRadius="full" disabled aria-label="Search">
  <SearchIcon />
</IconButton>

Spinner

  • 将 thickness 属性改为 borderWidth
  • 将 speed 属性改为 animationDuration

Before(迁移前)

tsx
<Spinner thickness="2px" speed="0.5s" />

After(迁移后)

tsx
<Spinner borderWidth="2px" animationDuration="0.5s" />

Dialog、Drawer

ModalDrawer 现在都使用复合组件,带有显式的 Positioner Portal 包裹。

属性变更(Dialog 与 Drawer 共用):

  • isOpen → open
  • onClose → onOpenChange(接收 { 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)
  • 尺寸 2xl–6xl → 映射为 xl

Drawer 专项变更:

  • placement="left" → placement="start"(支持 RTL)
  • placement="right" → placement="end"(支持 RTL)
  • isFullHeight → 给 Drawer.Content 添加 height="100%"
  • DrawerOverlay → Drawer.Backdrop
  • DrawerContent → Drawer.Positioner + Drawer.Content

已移除的属性: allowPinchZoom lockFocusAcrossFrames preserveScrollBarGap returnFocusOnCloseuseInert portalProps

Dialog 示例:

Before(迁移前)

tsx
import {
  Modal,
  ModalBody,
  ModalCloseButton,
  ModalContent,
  ModalFooter,
  ModalHeader,
  ModalOverlay,
} from "@chakra-ui/react"

const Demo = () => (
  <Modal isOpen={isOpen} onClose={onClose} isCentered closeOnEsc={false}>
    <ModalOverlay />
    <ModalContent>
      <ModalCloseButton />
      <ModalHeader>Title</ModalHeader>
      <ModalBody>Content</ModalBody>
      <ModalFooter>
        <Button onClick={onClose}>Close</Button>
      </ModalFooter>
    </ModalContent>
  </Modal>
)

After(迁移后)

tsx
import { Dialog, Portal } from "@chakra-ui/react"

const Demo = () => (
  <Dialog.Root
    open={isOpen}
    onOpenChange={(e) => !e.open && onClose()}
    placement="center"
    closeOnEscape={false}
  >
    <Portal>
      <Dialog.Backdrop />
      <Dialog.Positioner>
        <Dialog.Content>
          <Dialog.CloseTrigger />
          <Dialog.Header>Title</Dialog.Header>
          <Dialog.Body>Content</Dialog.Body>
          <Dialog.Footer>
            <Button onClick={onClose}>Close</Button>
          </Dialog.Footer>
        </Dialog.Content>
      </Dialog.Positioner>
    </Portal>
  </Dialog.Root>
)

Drawer 示例:

Before(迁移前)

tsx
import {
  Drawer,
  DrawerBody,
  DrawerCloseButton,
  DrawerContent,
  DrawerFooter,
  DrawerHeader,
  DrawerOverlay,
} from "@chakra-ui/react"

const Demo = () => (
  <Drawer isOpen={isOpen} placement="right" onClose={onClose} isFullHeight>
    <DrawerOverlay />
    <DrawerContent>
      <DrawerCloseButton />
      <DrawerHeader>Title</DrawerHeader>
      <DrawerBody>Content</DrawerBody>
      <DrawerFooter>
        <Button onClick={onClose}>Close</Button>
      </DrawerFooter>
    </DrawerContent>
  </Drawer>
)

After(迁移后)

tsx
import { Drawer, Portal } from "@chakra-ui/react"

const Demo = () => (
  <Drawer.Root
    open={isOpen}
    placement="end"
    onOpenChange={(e) => !e.open && onClose()}
  >
    <Portal>
      <Drawer.Backdrop />
      <Drawer.Positioner>
        <Drawer.Content height="100%">
          <Drawer.CloseTrigger />
          <Drawer.Header>Title</Drawer.Header>
          <Drawer.Body>Content</Drawer.Body>
          <Drawer.Footer>
            <Button onClick={onClose}>Close</Button>
          </Drawer.Footer>
        </Drawer.Content>
      </Drawer.Positioner>
    </Portal>
  </Drawer.Root>
)

Editable

现在使用点记法的复合组件。自定义控件使用声明式的触发器组件,而不是 useEditableControls 的 prop-getter 模式。

组件重命名:

  • Editable → Editable.Root
  • EditablePreview → Editable.Preview
  • EditableInput → Editable.Input
  • EditableTextarea → Editable.Textarea
  • useEditableControls → useEditableContext

属性变更:

  • isDisabled → disabled
  • onChange → onValueChange(接收 { value }
  • onSubmit → onValueCommit
  • onCancel → onValueRevert
  • startWithEditView → defaultEdit
  • selectAllOnFocus → selectOnFocus
  • submitOnBlur={false} → submitMode="enter"
  • finalFocusRef → finalFocusEl(返回元素的函数)
  • isPreviewFocusable={false} → 给 Editable.Preview 添加 tabIndex={undefined}

Before(迁移前)

tsx
import { Editable, EditableInput, EditablePreview } from "@chakra-ui/react"

const Demo = () => (
  <Editable
    defaultValue="Hello"
    isDisabled
    onSubmit={handleSubmit}
    onChange={handleChange}
    submitOnBlur={false}
    startWithEditView
  >
    <EditablePreview />
    <EditableInput />
  </Editable>
)

After(迁移后)

tsx
import { Editable } from "@chakra-ui/react"

const Demo = () => (
  <Editable.Root
    defaultValue="Hello"
    disabled
    onValueCommit={handleSubmit}
    onValueChange={handleChange}
    submitMode="enter"
    defaultEdit
  >
    <Editable.Preview />
    <Editable.Input />
  </Editable.Root>
)

自定义控件:

useEditableControls 的 prop-getter 模式已被声明式的触发器组件替代。

Before(迁移前)

tsx
function EditableControls() {
  const { isEditing, getSubmitButtonProps, getCancelButtonProps } =
    useEditableControls()
  return isEditing ? (
    <ButtonGroup size="sm">
      <IconButton icon={<CheckIcon />} {...getSubmitButtonProps()} />
      <IconButton icon={<CloseIcon />} {...getCancelButtonProps()} />
    </ButtonGroup>
  ) : null
}

After(迁移后)

tsx
<Editable.Control>
  <Editable.EditTrigger asChild>
    <IconButton variant="ghost" size="xs">
      <LuPencilLine />
    </IconButton>
  </Editable.EditTrigger>
  <Editable.CancelTrigger asChild>
    <IconButton variant="outline" size="xs">
      <LuX />
    </IconButton>
  </Editable.CancelTrigger>
  <Editable.SubmitTrigger asChild>
    <IconButton variant="outline" size="xs">
      <LuCheck />
    </IconButton>
  </Editable.SubmitTrigger>
</Editable.Control>

FormControl

Field 替换标准表单控件,用 Fieldset 替换分组控件(radio 组、checkbox 组)。 as='fieldset' 模式被替换为专门的 Fieldset 组件。

组件重命名:

  • FormControl → Field.Root
  • FormLabel → Field.Label
  • FormHelperText → Field.HelperText
  • FormErrorMessage → Field.ErrorText

对于 fieldset 用法:

  • FormControl as='fieldset' → Fieldset.Root
  • FormLabel as='legend' → Fieldset.Legend
  • FormHelperText → Fieldset.HelperText
  • FormErrorMessage → Fieldset.ErrorText

属性变更:

  • isInvalid → invalid
  • isRequired → required
  • isDisabled → disabled
  • isReadOnly → readOnly

Before(迁移前)

tsx
import {
  FormControl,
  FormErrorMessage,
  FormHelperText,
  FormLabel,
} from "@chakra-ui/react"

const Demo = () => (
  <FormControl isInvalid={isError}>
    <FormLabel>Email</FormLabel>
    <FormHelperText>We'll never share your email.</FormHelperText>
    <FormErrorMessage>Email is required.</FormErrorMessage>
  </FormControl>
)

After(迁移后)

tsx
import { Field } from "@chakra-ui/react"

const Demo = () => (
  <Field.Root invalid={isError}>
    <Field.Label>Email</Field.Label>
    <Field.HelperText>We'll never share your email.</Field.HelperText>
    <Field.ErrorText>Email is required.</Field.ErrorText>
  </Field.Root>
)

Field.ErrorText 只在 invalid true 时渲染,因此无需任何条件逻辑。

Fieldset 用法:

Before(迁移前)

tsx
import { FormControl, FormHelperText, FormLabel } from "@chakra-ui/react"

const Demo = () => (
  <FormControl as="fieldset">
    <FormLabel as="legend">Favorite Character</FormLabel>
    <FormHelperText>Select only if you're a fan.</FormHelperText>
  </FormControl>
)

After(迁移后)

tsx
import { Fieldset } from "@chakra-ui/react"

const Demo = () => (
  <Fieldset.Root>
    <Fieldset.Legend>Favorite Character</Fieldset.Legend>
    <Fieldset.HelperText>Select only if you're a fan.</Fieldset.HelperText>
  </Fieldset.Root>
)

Collapsible

Collapsible 组件替换。

Before(迁移前)

tsx
<Collapse in={isOpen} animateOpacity>
  Some content
</Collapse>

After(迁移后)

tsx
<Collapsible.Root open={isOpen}>
  <Collapsible.Content>Some content</Collapsible.Content>
</Collapsible.Root>

Fade、ScaleFade、Slide、SlideFade

所有过渡组件都被统一的 Presence 组件替代,它使用基于 CSS 的动画,而不是基于 JavaScript 的过渡。

组件映射:

  • Fade → Presence(animationName: fade-in / fade-out)
  • ScaleFade → Presence(animationStyle: scale-fade-in / scale-fade-out)
  • SlideFade → Presence(slide-from-bottom / slide-to-bottom 等)
  • Slide → Presence(按方向设置定位与动画)

属性变更:

  • in → present
  • initialScale → 已移除(缩放固定于 CSS keyframes)
  • offsetX / offsetY → 已移除(偏移固定于 CSS keyframes)
  • direction → 由定位属性与方向专属动画名称替代

Before(迁移前)

tsx
import { Fade, Slide } from "@chakra-ui/react"

const Demo = () => (
  <>
    <Fade in={isOpen}>
      <Box>Fading content</Box>
    </Fade>

    <Slide direction="bottom" in={isOpen}>
      <Box>Sliding content</Box>
    </Slide>
  </>
)

After(迁移后)

tsx
import { Presence } from "@chakra-ui/react"

const Demo = () => (
  <>
    <Presence
      present={isOpen}
      animationName={{ _open: "fade-in", _closed: "fade-out" }}
      animationDuration="moderate"
    >
      <Box>Fading content</Box>
    </Presence>

    <Presence
      present={isOpen}
      position="fixed"
      bottom="0"
      insetX="0"
      animationName={{
        _open: "slide-from-bottom-full",
        _closed: "slide-to-bottom-full",
      }}
      animationDuration="moderate"
    >
      <Box>Sliding content</Box>
    </Presence>
  </>
)

Slide 方向映射:

  • 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 已与 Slider 统一——传入数组值即可进行范围模式。两者现在都需要 Slider.Control 包裹,并且每个 thumb 内部都包含 Slider.HiddenInput

组件重命名:

  • Slider / RangeSlider → Slider.Root
  • SliderTrack / RangeSliderTrack → Slider.Track
  • SliderFilledTrack / RangeSliderFilledTrack → Slider.Range
  • SliderThumb / RangeSliderThumb → Slider.Thumb

属性变更:

  • onChange → onValueChange(接收 { value }
  • onChangeEnd → onValueChangeEnd(接收 { value }
  • onChangeStart → 已移除
  • colorScheme → colorPalette
  • isReversed / reversed → 已移除(改用 dir="rtl")
  • focusThumbOnChange → 已移除

Before(迁移前)

tsx
import {
  RangeSlider,
  RangeSliderFilledTrack,
  RangeSliderThumb,
  RangeSliderTrack,
} from "@chakra-ui/react"

const Demo = () => (
  <RangeSlider defaultValue={[10, 30]} onChange={(val) => console.log(val)}>
    <RangeSliderTrack>
      <RangeSliderFilledTrack />
    </RangeSliderTrack>
    <RangeSliderThumb index={0} />
    <RangeSliderThumb index={1} />
  </RangeSlider>
)

After(迁移后)

tsx
import { Slider } from "@chakra-ui/react"

const Demo = () => (
  <Slider.Root
    defaultValue={[10, 30]}
    onValueChange={(e) => console.log(e.value)}
  >
    <Slider.Control>
      <Slider.Track>
        <Slider.Range />
      </Slider.Track>
      <Slider.Thumb index={0}>
        <Slider.HiddenInput />
      </Slider.Thumb>
      <Slider.Thumb index={1}>
        <Slider.HiddenInput />
      </Slider.Thumb>
    </Slider.Control>
  </Slider.Root>
)

Table

  • TableContainer 现在叫 Table.ScrollArea

  • Td(现在叫 Table.Cell)的 isNumeric 现在改为 textAlign="end"

  • Th 现在叫 Table.ColumnHeader

复合组件的名称略有调整。

Before(迁移前)

tsx
<Table variant="simple">
  <TableCaption>Imperial to metric conversion factors</TableCaption>
  <Thead>
    <Tr>
      <Th>Product</Th>
      <Th>Category</Th>
      <Th isNumeric>Price</Th>
    </Tr>
  </Thead>
  <Tbody>
    {items.map((item) => (
      <Tr key={item.id}>
        <Td>{item.name}</Td>
        <Td>{item.category}</Td>
        <Td isNumeric>{item.price}</Td>
      </Tr>
    ))}
  </Tbody>
  <Tfoot>
    <Tr>
      <Th>Product</Th>
      <Th>Category</Th>
      <Th isNumeric>Price</Th>
    </Tr>
  </Tfoot>
</Table>

After(迁移后)

tsx
<Table.Root size="sm">
  <Table.Header>
    <Table.Row>
      <Table.ColumnHeader>Product</Table.ColumnHeader>
      <Table.ColumnHeader>Category</Table.ColumnHeader>
      <Table.ColumnHeader textAlign="end">Price</Table.ColumnHeader>
    </Table.Row>
  </Table.Header>
  <Table.Body>
    {items.map((item) => (
      <Table.Row key={item.id}>
        <Table.Cell>{item.name}</Table.Cell>
        <Table.Cell>{item.category}</Table.Cell>
        <Table.Cell textAlign="end">{item.price}</Table.Cell>
      </Table.Row>
    ))}
  </Table.Body>
</Table.Root>

Tag

TagLeftIconTagRightIcon 现在是 Tag.StartElement Tag.EndElement

Before(迁移前)

tsx
<Tag>
  <TagLeftIcon boxSize="12px" as={AddIcon} />
  <TagLabel>Cyan</TagLabel>
  <TagRightIcon boxSize="12px" as={AddIcon} />
</Tag>

After(迁移后)

tsx
<Tag.Root>
  <Tag.StartElement>
    <AddIcon />
  </Tag.StartElement>
  <Tag.Label>Cyan</Tag.Label>
  <Tag.EndElement>
    <AddIcon />
  </Tag.EndElement>
</Tag.Root>

TagCloseButton 现在是 Tag.CloseTrigger

Before(迁移前)

tsx
<Tag>
  <TagLabel>Green</TagLabel>
  <TagCloseButton />
</Tag>

After(迁移后)

tsx
<Tag.Root>
  <Tag.Label>Green</Tag.Label>
  <Tag.CloseTrigger />
</Tag.Root>

Alert

现在使用点记法的复合组件。v3 还引入了 Alert.Content 作为标题与描述的包裹。

组件重命名:

  • Alert → Alert.Root
  • AlertIcon → Alert.Indicator
  • AlertTitle → Alert.Title
  • AlertDescription → Alert.Description

属性变更:

  • 移除了 addRole 属性(v3 中 role 会自动处理)

Before(迁移前)

tsx
import {
  Alert,
  AlertDescription,
  AlertIcon,
  AlertTitle,
} from "@chakra-ui/react"

const Demo = () => (
  <Alert status="error">
    <AlertIcon />
    <AlertTitle>Your browser is outdated!</AlertTitle>
    <AlertDescription>Your Chakra experience may be degraded.</AlertDescription>
  </Alert>
)

After(迁移后)

tsx
import { Alert } from "@chakra-ui/react"

const Demo = () => (
  <Alert.Root status="error">
    <Alert.Indicator />
    <Alert.Content>
      <Alert.Title>Your browser is outdated!</Alert.Title>
      <Alert.Description>
        Your Chakra experience may be degraded.
      </Alert.Description>
    </Alert.Content>
  </Alert.Root>
)

变体变更:

left-accenttop-accent 变体已被移除。请在 Alert.Root 上使用边框样式属性来复现效果:

  • left-accent → variant="subtle" + borderStartWidth="3px" + borderStartColor="colorPalette.solid"
  • top-accent → variant="subtle" + borderTopWidth="3px" + borderTopColor="colorPalette.solid"

新增了 surfaceoutline 变体。

Before(迁移前)

tsx
<Alert status="success" variant="left-accent">
  <AlertIcon />
  Data uploaded to the server. Fire on!
</Alert>

After(迁移后)

tsx
<Alert.Root
  status="success"
  variant="subtle"
  borderStartWidth="3px"
  borderStartColor="colorPalette.solid"
>
  <Alert.Indicator />
  Data uploaded to the server. Fire on!
</Alert.Root>

Skeleton

startColorendColor 属性现在使用 CSS 变量。

Before(迁移前)

tsx
<Skeleton startColor="pink.500" endColor="orange.500" />

After(迁移后)

tsx
<Skeleton
  css={{
    "--start-color": "colors.pink.500",
    "--end-color": "colors.orange.500",
  }}
/>

isLoaded 属性现在叫 loading

Before(迁移前)

tsx
<Skeleton isLoaded>
  <span>Chakra ui is cool</span>
</Skeleton>

After(迁移后)

tsx
<Skeleton loading={false}>
  <span>Chakra ui is cool</span>
</Skeleton>

Stepper

已改名为 Steps,并采用复合组件模式。 useSteps hook 仍然可用,但 API 已更新。

组件重命名:

  • Stepper → Steps.Root
  • Step → Steps.Item
  • StepIndicator → Steps.Indicator
  • StepStatus → Steps.Status
  • StepTitle → Steps.Title
  • StepDescription → Steps.Description
  • StepSeparator → Steps.Separator

属性变更:

  • index → step
  • 子元素必须包裹在 Steps.List 中

Hook 变更:

  • useSteps({ index }) → useSteps({ defaultStep })
  • 使用 useSteps 时,用 Steps.RootProvider 传入 value={stepsApi},而不是 Steps.Root

Before(迁移前)

tsx
import {
  Step,
  StepIcon,
  StepIndicator,
  StepNumber,
  StepSeparator,
  StepStatus,
  StepTitle,
  Stepper,
} from "@chakra-ui/react"

const Demo = () => (
  <Stepper index={1}>
    {steps.map((step, index) => (
      <Step key={index}>
        <StepIndicator>
          <StepStatus complete={<StepIcon />} incomplete={<StepNumber />} />
        </StepIndicator>
        <StepTitle>{step.title}</StepTitle>
        <StepSeparator />
      </Step>
    ))}
  </Stepper>
)

After(迁移后)

tsx
import { Steps } from "@chakra-ui/react"

const Demo = () => (
  <Steps.Root step={1}>
    <Steps.List>
      {steps.map((step, index) => (
        <Steps.Item key={index}>
          <Steps.Indicator>
            <Steps.Status complete={<StepIcon />} incomplete={<StepNumber />} />
          </Steps.Indicator>
          <Steps.Title>{step.title}</Steps.Title>
          <Steps.Separator />
        </Steps.Item>
      ))}
    </Steps.List>
  </Steps.Root>
)

Stat

现在使用点记法的复合组件。

组件重命名:

  • 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 内部嵌套子 Stat.Root

Before(迁移前)

tsx
import {
  Stat,
  StatArrow,
  StatHelpText,
  StatLabel,
  StatNumber,
} from "@chakra-ui/react"

const Demo = () => (
  <Stat>
    <StatLabel>Revenue</StatLabel>
    <StatNumber>$45,670</StatNumber>
    <StatHelpText>
      <StatArrow type="increase" />
      12.5%
    </StatHelpText>
  </Stat>
)

After(迁移后)

tsx
import { Stat } from "@chakra-ui/react"

const Demo = () => (
  <Stat.Root>
    <Stat.Label>Revenue</Stat.Label>
    <Stat.ValueText>$45,670</Stat.ValueText>
    <Stat.HelpText>
      <Stat.UpIndicator />
      12.5%
    </Stat.HelpText>
  </Stat.Root>
)

Menu

现在在所有地方都使用复合组件。

Before(迁移前)

tsx
<Menu>
  <MenuButton as={Button} rightIcon={<ChevronDownIcon />}>
    Actions
  </MenuButton>
  <MenuList>
    <MenuItem>Download</MenuItem>
    <MenuItem>Create a Copy</MenuItem>
  </MenuList>
</Menu>

After(迁移后)

tsx
<Menu.Root>
  <Menu.Trigger asChild>
    <Button>
      Actions
      <ChevronDownIcon />
    </Button>
  </Menu.Trigger>
  <Portal>
    <Menu.Positioner>
      <Menu.Content>
        <Menu.Item value="download">Download</Menu.Item>
        <Menu.Item value="copy">Create a Copy</Menu.Item>
      </Menu.Content>
    </Menu.Positioner>
  </Portal>
</Menu.Root>

现在可以通过 Menu.Context 访问内部状态,不再使用 render prop。

Before(迁移前)

tsx
<Menu>
  {({ isOpen }) => (
    <>
      <MenuButton isActive={isOpen} as={Button} rightIcon={<ChevronDownIcon />}>
        {isOpen ? "Close" : "Open"}
      </MenuButton>
      <MenuList>
        <MenuItem>Download</MenuItem>
        <MenuItem onClick={() => alert("Kagebunshin")}>Create a Copy</MenuItem>
      </MenuList>
    </>
  )}
</Menu>

After(迁移后)

tsx
<Menu.Root>
  <Menu.Context>
    {(menu) => (
      <Menu.Trigger asChild>
        <Button>
          {menu.open ? "Close" : "Open"}
          <ChevronDownIcon />
        </Button>
      </Menu.Trigger>
    )}
  </Menu.Context>
  <Portal>
    <Menu.Positioner>
      <Menu.Content>
        <Menu.Item value="download">Download</Menu.Item>
        <Menu.Item value="copy" onSelect={() => alert("Naruto")}>
          Create a Copy
        </Menu.Item>
      </Menu.Content>
    </Menu.Positioner>
  </Portal>
</Menu.Root>
  • Menu 上的 isLazy 属性被拆分为 Menu.RootlazyMount unmountOnExit

  • MenuOptionGroup 被拆分为 Menu.RadioItemGroup Menu.CheckboxItemGroup,分别处理各自的状态。

Before(迁移前)

tsx
<Menu>
  <MenuButton as={Button}>Trigger</MenuButton>
  <MenuList>
    <MenuOptionGroup defaultValue="asc" title="Order" type="radio">
      <MenuItemOption value="asc">Ascending</MenuItemOption>
      <MenuItemOption value="desc">Descending</MenuItemOption>
    </MenuOptionGroup>
    <MenuDivider />
    <MenuOptionGroup title="Country" type="checkbox">
      <MenuItemOption value="email">Email</MenuItemOption>
      <MenuItemOption value="phone">Phone</MenuItemOption>
      <MenuItemOption value="country">Country</MenuItemOption>
    </MenuOptionGroup>
  </MenuList>
</Menu>

After(迁移后)

tsx
<Menu.Root>
  <Menu.Trigger asChild>
    <Button>Trigger</Button>
  </Menu.Trigger>
  <Portal>
    <Menu.Positioner>
      <Menu.Content minW="10rem">
        <Menu.RadioItemGroup defaultValue="asc">
          <Menu.RadioItem value="asc">Ascending</Menu.RadioItem>
          <Menu.RadioItem value="desc">Descending</Menu.RadioItem>
        </Menu.RadioItemGroup>
        <Menu.CheckboxItemGroup defaultValue={["email"]}>
          <Menu.CheckboxItem value="email">Email</Menu.CheckboxItem>
          <Menu.CheckboxItem value="phone">Phone</Menu.CheckboxItem>
          <Menu.CheckboxItem value="country">Country</Menu.CheckboxItem>
        </Menu.CheckboxItemGroup>
      </Menu.Content>
    </Menu.Positioner>
  </Portal>
</Menu.Root>

Tooltip

现在是一个 snippet 组件,从 @/components/ui/tooltip 导入,而不是从 @chakra-ui/react 导入。

属性变更:

  • label → content
  • hasArrow → showArrow
  • closeOnEsc → closeOnEscape
  • closeOnMouseDown → closeOnPointerDown
  • onOpen / onClose → onOpenChange(接收 { open }
  • shouldWrapChildren → 手动用 <span> 包裹 children
  • placement、gutter、offset、arrowPadding → 合并到 positioning 对象

已移除的属性: modifiers motionPropsportalProps arrowSizearrowShadowColor

Before(迁移前)

tsx
import { Tooltip } from "@chakra-ui/react"

<Tooltip label="Info" hasArrow placement="top" closeOnEsc={false}>
  <button>Hover</button>
</Tooltip>

After(迁移后)

tsx
import { Tooltip } from "@/components/ui/tooltip"

<Tooltip
  content="Info"
  showArrow
  positioning={{ placement: "top" }}
  closeOnEscape={false}
>
  <button>Hover</button>
</Tooltip>

Accordion

现在使用点记法的复合组件。所有子组件都归属在 Accordion 命名空间下。

组件重命名:

  • Accordion → Accordion.Root
  • AccordionItem → Accordion.Item(现在必填 value 属性)
  • AccordionButton → Accordion.ItemTrigger
  • AccordionIcon → Accordion.ItemIndicator
  • AccordionPanel → Accordion.ItemContent + Accordion.ItemBody

属性变更:

  • allowMultiple → multiple
  • allowToggle → collapsible
  • defaultIndex → defaultValue(现在为字符串数组)
  • index → value(现在为字符串数组)
  • onChange → onValueChange

Before(迁移前)

tsx
import {
  Accordion,
  AccordionButton,
  AccordionIcon,
  AccordionItem,
  AccordionPanel,
  Box,
} from "@chakra-ui/react"

const Demo = () => (
  <Accordion allowToggle>
    <AccordionItem>
      <h2>
        <AccordionButton>
          <Box as="span" flex="1" textAlign="left">
            Section 1 title
          </Box>
          <AccordionIcon />
        </AccordionButton>
      </h2>
      <AccordionPanel pb={4}>Lorem ipsum dolor sit amet.</AccordionPanel>
    </AccordionItem>
  </Accordion>
)

After(迁移后)

tsx
import { Accordion, Box } from "@chakra-ui/react"

const Demo = () => (
  <Accordion.Root collapsible>
    <Accordion.Item value="section-1">
      <h2>
        <Accordion.ItemTrigger>
          <Box as="span" flex="1" textAlign="left">
            Section 1 title
          </Box>
          <Accordion.ItemIndicator />
        </Accordion.ItemTrigger>
      </h2>
      <Accordion.ItemContent>
        <Accordion.ItemBody pb={4}>
          Lorem ipsum dolor sit amet.
        </Accordion.ItemBody>
      </Accordion.ItemContent>
    </Accordion.Item>
  </Accordion.Root>
)

Render Props → Context

AccordionItem 的 render prop 模式( {({ isExpanded }) => ... } )已被替换。请改用 Accordion.ItemContext 组件,或 useAccordionItemContext hook。 isExpanded 属性现在叫 expanded

Before(迁移前)

tsx
<AccordionItem>
  {({ isExpanded }) => (
    <>
      <AccordionButton>
        <Box flex="1" textAlign="left">
          Section title
        </Box>
        {isExpanded ? <MinusIcon /> : <AddIcon />}
      </AccordionButton>
      <AccordionPanel>Content</AccordionPanel>
    </>
  )}
</AccordionItem>

After(迁移后)

tsx
<Accordion.Item value="section-1">
  <Accordion.ItemContext>
    {({ expanded }) => (
      <>
        <Accordion.ItemTrigger>
          <Box flex="1" textAlign="left">
            Section title
          </Box>
          {expanded ? <LuMinus /> : <LuPlus />}
        </Accordion.ItemTrigger>
        <Accordion.ItemContent>
          <Accordion.ItemBody>Content</Accordion.ItemBody>
        </Accordion.ItemContent>
      </>
    )}
  </Accordion.ItemContext>
</Accordion.Item>

Tabs

组件结构已更改,list 和 panels 现在必须提供必填的 value 属性。

Before(迁移前)

tsx
<Tabs>
  <TabList>
    <Tab>One</Tab>
    <Tab>Two</Tab>
    <Tab>Three</Tab>
  </TabList>
  <TabPanels>
    <TabPanel>one!</TabPanel>
    <TabPanel>two!</TabPanel>
    <TabPanel>three!</TabPanel>
  </TabPanels>
</Tabs>

After(迁移后)

tsx
<Tabs.Root>
  <Tabs.List>
    <Tabs.Trigger value="one">One</Tabs.Trigger>
    <Tabs.Trigger value="two">Two</Tabs.Trigger>
    <Tabs.Trigger value="three">Three</Tabs.Trigger>
  </Tabs.List>
  <Tabs.Content value="one">one!</Tabs.Content>
  <Tabs.Content value="two">two!</Tabs.Content>
  <Tabs.Content value="three">three!</Tabs.Content>
</Tabs.Root>

defaultIndexindex onChange 现在是 defaultValue valueonValueChange

Before(迁移前)

tsx
<Tabs defaultIndex={0} index={0} onChange={(index) => {}} />

After(迁移后)

tsx
<Tabs defaultValue={0} value={0} onValueChange={({ value }) => {}} />

TabsisLazy 属性现在是 lazyMountunmountOnExit

Before(迁移前)

tsx
<Tabs isLazy />

After(迁移后)

tsx
<Tabs.Root lazyMount unmountOnExit />

Show 与 Hide

ShowHide 组件已被移除,改用 hideFromhideBelow

Before(迁移前)

tsx
<Show below="md">
  This text appears only on screens md and smaller.
</Show>

<Hide below="md">
  This text hides at the "md" value screen width and smaller.
</Hide>

After(迁移后)

tsx
<Box hideBelow="md">
  This text hides at the "md" value screen width and smaller.
</Box>

<Box hideFrom="md">
  This text only shows on screens md and larger.
</Box>

Checkbox

已重构为复合组件。单一的 <Checkbox> 现在拆分为显式的部件,以便对结构与样式进行完全控制。

属性变更:

  • isChecked → checked
  • isDisabled → disabled
  • isInvalid → invalid
  • isReadOnly → readOnly
  • isIndeterminate → checked="indeterminate"
  • onChange → onCheckedChange
  • colorScheme → colorPalette
  • icon → 渲染为 Checkbox.Control 的 children
  • iconColor → Checkbox.Indicator 上的 color
  • iconSize → Checkbox.Indicator 上的 boxSize
  • isFocusable → 已移除

CheckboxGroup:

  • isDisabled → disabled
  • onChange → onValueChange
  • isNative → 已移除

Before(迁移前)

tsx
import { Checkbox } from "@chakra-ui/react"

const Demo = () => (
  <Checkbox
    isChecked={checked}
    isIndeterminate={indeterminate}
    onChange={(e) => setChecked(e.target.checked)}
    colorScheme="blue"
  >
    Accept terms
  </Checkbox>
)

After(迁移后)

tsx
import { Checkbox } from "@chakra-ui/react"

const Demo = () => (
  <Checkbox.Root
    checked={indeterminate ? "indeterminate" : checked}
    onCheckedChange={(e) => setChecked(e.checked === true)}
    colorPalette="blue"
  >
    <Checkbox.HiddenInput />
    <Checkbox.Control>
      <Checkbox.Indicator />
    </Checkbox.Control>
    <Checkbox.Label>Accept terms</Checkbox.Label>
  </Checkbox.Root>
)

Radio Group

已重构为复合组件。Radio 现在是 RadioGroup.Item,带有显式的子组件: ItemHiddenInputItemIndicator ItemText

组件重命名:

  • RadioGroup → RadioGroup.Root
  • Radio → RadioGroup.Item(带必填子组件)

RadioGroup 属性变更:

  • onChange → onValueChange(接收 { value } 对象)
  • colorScheme → colorPalette

Radio 属性变更:

  • isDisabled → disabled
  • isInvalid、isChecked、defaultChecked → 已移除(由 Root 控制)
  • colorScheme → 已从 item 移除(请在 Root 上设置 colorPalette)
  • inputProps → 展开到 RadioGroup.ItemHiddenInput

Before(迁移前)

tsx
import { Radio, RadioGroup } from "@chakra-ui/react"

const Demo = () => (
  <RadioGroup defaultValue="2" onChange={(val) => setValue(val)}>
    <Radio value="1">Option 1</Radio>
    <Radio value="2">Option 2</Radio>
  </RadioGroup>
)

After(迁移后)

tsx
import { RadioGroup } from "@chakra-ui/react"

const Demo = () => (
  <RadioGroup.Root defaultValue="2" onValueChange={(e) => setValue(e.value)}>
    <RadioGroup.Item value="1">
      <RadioGroup.ItemHiddenInput />
      <RadioGroup.ItemIndicator />
      <RadioGroup.ItemText>Option 1</RadioGroup.ItemText>
    </RadioGroup.Item>
    <RadioGroup.Item value="2">
      <RadioGroup.ItemHiddenInput />
      <RadioGroup.ItemIndicator />
      <RadioGroup.ItemText>Option 2</RadioGroup.ItemText>
    </RadioGroup.Item>
  </RadioGroup.Root>
)

Button 属性汇总

  • isActive → data-active 属性
  • isDisabled → disabled
  • isLoading → loading
  • leftIcon / rightIcon → 作为 children 传入
  • iconSpacing → 已移除(使用 flex 布局中的 gap)
  • colorScheme → colorPalette

示例:

tsx
// Before
<Button
  isActive={true}
  isDisabled={false}
  isLoading={true}
  leftIcon={<Icon />}
  rightIcon={<Icon />}
  colorScheme="blue"
>
  Submit
</Button>

// After
<Button
  data-active=""
  disabled={false}
  loading={true}
  colorPalette="blue"
>
  <LeftIcon />
  Submit
  <RightIcon />
</Button>

Input 属性汇总

  • isDisabled → disabled
  • isInvalid → invalid
  • isReadOnly → readOnly
  • isRequired → required
  • colorScheme → colorPalette
  • focusBorderColor → 使用 CSS 变量
  • errorBorderColor → 使用 CSS 变量
tsx
// Before
<Input
  isDisabled={false}
  isInvalid={true}
  isReadOnly={false}
  isRequired={true}
  colorScheme="blue"
  focusBorderColor="blue.500"
  errorBorderColor="red.500"
/>

// After
<Input
  disabled={false}
  invalid={true}
  readOnly={false}
  required={true}
  colorPalette="blue"
  style={{
    "--focus-color": "blue.500",
    "--error-color": "red.500"
  }}
/>

Checkbox 属性汇总

  • isChecked → checked
  • isDisabled → disabled
  • isInvalid → invalid
  • isIndeterminate → checked="indeterminate"
  • onChange → onCheckedChange
  • colorScheme → colorPalette
  • iconColor → Checkbox.Indicator 上的 color
  • iconSize → Checkbox.Indicator 上的 boxSize
  • isFocusable → 已移除

Modal 到 Dialog 属性汇总

  • isOpen → open
  • onClose → onOpenChange(接收 { open }
  • isCentered → placement="center"
  • closeOnOverlayClick → closeOnInteractOutside
  • closeOnEsc → closeOnEscape
  • blockScrollOnMount → preventScroll
  • onOverlayClick → onInteractOutside
  • onEsc → onEscapeKeyDown
  • onCloseComplete → onExitComplete
  • initialFocusRef → initialFocusEl(返回元素的函数)
  • finalFocusRef → finalFocusEl(返回元素的函数)
  • scrollBehavior → 不变
  • motionPreset → 不变
  • trapFocus → 不变
  • 尺寸 2xl–6xl → 映射为 xl
  • 已移除:allowPinchZoom、lockFocusAcrossFrames、preserveScrollBarGap、returnFocusOnClose、useInert、portalProps

Stack 属性汇总

  • spacing → gap
  • divider → separator
  • 其他属性保持不变
tsx
// Before
<Stack
  spacing="4"
  divider={<StackDivider />}
>
  <Box>Item 1</Box>
  <Box>Item 2</Box>
</Stack>

// After
<Stack
  gap="4"
  separator={<Separator />}
>
  <Box>Item 1</Box>
  <Box>Item 2</Box>
</Stack>