Accordion
Accordion 从 HeroUI v2 到 v3 的迁移指南。
完整的 API 参考、样式指南与高级示例请参阅 v3 Accordion 文档。本指南只关注从 HeroUI v2 的迁移。
结构变化
在 v2 中,AccordionItem 是一个独立组件,通过 props 接收标题、副标题、内容等元素:
import { Accordion, AccordionItem } from "@heroui/react";
export default function App() {
return (
<Accordion>
<AccordionItem
key="1"
title="Accordion 1"
>
Content here
</AccordionItem>
<AccordionItem
key="2"
title="Accordion 2"
>
Content here
</AccordionItem>
</Accordion>
);
}在 v3 中,Accordion 采用复合组件模式,并使用显式子组件进行组合:
import { Accordion } from "@heroui/react";
export default function App() {
return (
<Accordion>
<Accordion.Item id="1">
<Accordion.Heading>
<Accordion.Trigger>
Accordion 1
<Accordion.Indicator />
</Accordion.Trigger>
</Accordion.Heading>
<Accordion.Panel>
<Accordion.Body>Content here</Accordion.Body>
</Accordion.Panel>
</Accordion.Item>
<Accordion.Item id="2">
<Accordion.Heading>
<Accordion.Trigger>
Accordion 2
<Accordion.Indicator />
</Accordion.Trigger>
</Accordion.Heading>
<Accordion.Panel>
<Accordion.Body>Content here</Accordion.Body>
</Accordion.Panel>
</Accordion.Item>
</Accordion>
);
}主要变化
1. 组件结构
v2: 单个带 props 的 AccordionItem 组件
v3: 复合组件:Accordion.Item、Accordion.Heading、Accordion.Trigger、Accordion.Panel、Accordion.Body、Accordion.Indicator
2. Prop 变更
| v2 prop | v3 位置 | 说明 |
|---|---|---|
selectedKeys | Accordion | 已重命名为 expandedKeys;类型为 Iterable<Key> |
defaultSelectedKeys | Accordion | 已重命名为 defaultExpandedKeys |
onSelectionChange | Accordion | 已重命名为 onExpandedChange;签名为 (keys: Set<Key>) => void |
selectionMode="multiple" | Accordion | 使用 allowsMultipleExpanded(布尔值) |
isCompact | — | 已移除(请使用 Tailwind CSS,例如 text-sm、py-2) |
hideIndicator | — | 不渲染 <Accordion.Indicator /> 即可隐藏 |
disableAnimation、disableIndicatorAnimation、motionProps | — | 已移除(不支持动画 / 请使用 CSS) |
showDivider、dividerProps | — | 已移除(请手动添加分隔线或使用 Divider) |
keepContentMounted | — | 已移除(v3 中内容始终挂载) |
selectionBehavior、disallowEmptySelection | — | 已移除(不适用) |
itemClasses | — | 在各项上使用 className |
startContent | — | 将内容放在 <Accordion.Trigger> 内 |
title、subtitle | — | 将内容放在 <Accordion.Trigger> 内 |
3. 变体
v2 变体: light、shadow、bordered、splitted
v3 变体: default、surface
v3 的变体更精简。若要接近 v2 的视觉效果:
- v2
light→ v3default - v2
shadow→ v3surface - v2
bordered→ v3default+ 添加边框类 - v2
splitted→ v3default+ 为各项添加背景色与间距 / 外边距
4. 条目标识
v2: React 的 key 同时用于列表调和与展开状态。
v3: 在 Accordion.Item 上使用 id 标识展开状态;列表中仍可为各项保留 React 的 key。
迁移示例
受控状态
import { useState } from "react";
import { Accordion, AccordionItem } from "@heroui/react";
const [selectedKeys, setSelectedKeys] = useState(new Set(["1"]));
<Accordion
selectedKeys={selectedKeys}
onSelectionChange={setSelectedKeys}
>
<AccordionItem key="1" title="Item 1">
Content 1
</AccordionItem>
<AccordionItem key="2" title="Item 2">
Content 2
</AccordionItem>
</Accordion>import { useState } from "react";
import { Accordion } from "@heroui/react";
import type { Key } from "@heroui/react";
const [expandedKeys, setExpandedKeys] = useState<Set<Key>>(new Set(["1"]));
<Accordion
expandedKeys={expandedKeys}
onExpandedChange={setExpandedKeys}
>
<Accordion.Item id="1">
<Accordion.Heading>
<Accordion.Trigger>
Item 1
<Accordion.Indicator />
</Accordion.Trigger>
</Accordion.Heading>
<Accordion.Panel>
<Accordion.Body>Content 1</Accordion.Body>
</Accordion.Panel>
</Accordion.Item>
<Accordion.Item id="2">
<Accordion.Heading>
<Accordion.Trigger>
Item 2
<Accordion.Indicator />
</Accordion.Trigger>
</Accordion.Heading>
<Accordion.Panel>
<Accordion.Body>Content 2</Accordion.Body>
</Accordion.Panel>
</Accordion.Item>
</Accordion>副标题与前置内容
import { Icon } from "@iconify/react";
<Accordion>
<AccordionItem
key="1"
title="Accordion 1"
subtitle="Press to expand"
startContent={<Icon icon="gravity-ui:box" />}
>
Content here
</AccordionItem>
</Accordion>import { Icon } from "@iconify/react";
<Accordion>
<Accordion.Item id="1">
<Accordion.Heading>
<Accordion.Trigger>
<Icon icon="gravity-ui:box" className="mr-2" />
<div className="flex flex-col">
<span>Accordion 1</span>
<span className="text-sm text-muted">Press to expand</span>
</div>
<Accordion.Indicator />
</Accordion.Trigger>
</Accordion.Heading>
<Accordion.Panel>
<Accordion.Body>Content here</Accordion.Body>
</Accordion.Panel>
</Accordion.Item>
</Accordion>自定义指示器
import { Icon } from "@iconify/react";
<Accordion>
<AccordionItem
key="1"
title="Item 1"
indicator={(props) => (
props.isOpen ? <Icon icon="mdi:minus" /> : <Icon icon="mdi:plus" />
)}
>
Content
</AccordionItem>
</Accordion>import { Icon } from "@iconify/react";
import { useState } from "react";
import { Accordion } from "@heroui/react";
import type { Key } from "@heroui/react";
const [expandedKeys, setExpandedKeys] = useState<Set<Key>>(new Set());
<Accordion
expandedKeys={expandedKeys}
onExpandedChange={setExpandedKeys}
>
<Accordion.Item id="1">
<Accordion.Heading>
<Accordion.Trigger>
Item 1
<Accordion.Indicator>
{expandedKeys.has("1") ? (
<Icon icon="gravity-ui:minus" />
) : (
<Icon icon="gravity-ui:plus" />
)}
</Accordion.Indicator>
</Accordion.Trigger>
</Accordion.Heading>
<Accordion.Panel>
<Accordion.Body>Content</Accordion.Body>
</Accordion.Panel>
</Accordion.Item>
</Accordion>禁用项与默认展开键
<Accordion
defaultExpandedKeys={["1"]}
disabledKeys={["2"]}
>
<AccordionItem key="1" title="Item 1">
Content 1
</AccordionItem>
<AccordionItem key="2" title="Item 2">
Content 2
</AccordionItem>
</Accordion><Accordion defaultExpandedKeys={["1"]}>
<Accordion.Item id="1">
<Accordion.Heading>
<Accordion.Trigger>
Item 1
<Accordion.Indicator />
</Accordion.Trigger>
</Accordion.Heading>
<Accordion.Panel>
<Accordion.Body>Content 1</Accordion.Body>
</Accordion.Panel>
</Accordion.Item>
<Accordion.Item id="2" isDisabled>
<Accordion.Heading>
<Accordion.Trigger>
Item 2
<Accordion.Indicator />
</Accordion.Trigger>
</Accordion.Heading>
<Accordion.Panel>
<Accordion.Body>Content 2</Accordion.Body>
</Accordion.Panel>
</Accordion.Item>
</Accordion>样式变化
v2:classNames prop
<AccordionItem
classNames={{
base: "custom-base",
title: "custom-title",
content: "custom-content"
}}
/>v3:在各子组件上使用 className
<Accordion.Item className="custom-base">
<Accordion.Heading>
<Accordion.Trigger className="custom-title">
Title
<Accordion.Indicator />
</Accordion.Trigger>
</Accordion.Heading>
<Accordion.Panel>
<Accordion.Body className="custom-content">
Content
</Accordion.Body>
</Accordion.Panel>
</Accordion.Item>组件剖析
v3 Accordion 的结构如下:
Accordion (Root)
└── Accordion.Item
├── Accordion.Heading
│ └── Accordion.Trigger
│ ├── [Your content: title, subtitle, icons, etc.]
│ └── Accordion.Indicator (optional)
└── Accordion.Panel
└── Accordion.Body
└── [Your content]总结
- 组件结构:必须使用复合组件,而不是仅靠 props 拼装。
- 状态 props:
selectedKeys→expandedKeys,onSelectionChange→onExpandedChange。 - 多段展开:
selectionMode="multiple"→allowsMultipleExpanded={true}。 - 条目标识:展开状态用
id;列表调和仍使用 React 的key。 - 变体:由 4 种缩减为 2 种。
- 已移除的 props:大量便捷 props 已移除;请改用 Tailwind CSS 类。
- 内容结构:标题、副标题与前置内容需手动放入
Trigger。 - 指示器:必须显式渲染;不再自动生成指示器。