Skip to content
Lenso UI

Accordion

Migration guide for Accordion from HeroUI v2 to v3

Refer to the v3 Accordion documentation for complete API reference, styling guide, and advanced examples. This guide only focuses on migrating from HeroUI v2.

Structure Changes

In v2, AccordionItem was a self-contained component that accepted props for title, subtitle, content, and other elements:

import { Accordion, AccordionItem } from "@lenso/ui";
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>  );}

In v3, Accordion uses a compound component pattern with explicit subcomponents:

import { Accordion } from "@lenso/ui";
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>  );}

Key Changes

1. Component Structure

v2: Single AccordionItem component with props
v3: Compound components: Accordion.Item, Accordion.Heading, Accordion.Trigger, Accordion.Panel, Accordion.Body, Accordion.Indicator

2. Prop Changes

v2 Propv3 LocationNotes
selectedKeysAccordionRenamed to expandedKeys; type Iterable<Key>
defaultSelectedKeysAccordionRenamed to defaultExpandedKeys
onSelectionChangeAccordionRenamed to onExpandedChange; (keys: Set<Key>) => void
selectionMode="multiple"AccordionUse allowsMultipleExpanded (boolean)
isCompact-Removed (use Tailwind CSS e.g. text-sm, py-2)
hideIndicator-Omit <Accordion.Indicator /> to hide
disableAnimation, disableIndicatorAnimation, motionProps-Removed (animations not supported / use CSS)
showDivider, dividerProps-Removed (add dividers manually or use Divider)
keepContentMounted-Removed (content always mounted in v3)
selectionBehavior, disallowEmptySelection-Removed (not applicable)
itemClasses-Use className on items
startContent-Place content in <Accordion.Trigger>
title, subtitle-Place content in <Accordion.Trigger>

3. Variants

v2 Variants: light, shadow, bordered, splitted
v3 Variants: default, surface

The v3 variants are simplified. To achieve similar effects to v2 variants:

  • v2 light → v3 default
  • v2 shadow → v3 surface
  • v2 bordered → v3 default + add border classes
  • v2 splitted → v3 default + add background color and spacing/margin between items

4. Item Identification

v2: React's key was used for both list reconciliation and expanded state.
v3: Use id on Accordion.Item for expanded state; keep React's key on items in lists.

Migration Examples

Controlled State

import { useState } from "react";import { Accordion, AccordionItem } from "@lenso/ui";
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>

With Subtitle and Start Content

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>

Custom Indicator

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>

Disabled Items and Default Expanded Keys

<Accordion   defaultExpandedKeys={["1"]}  disabledKeys={["2"]}>  <AccordionItem key="1" title="Item 1">    Content 1  </AccordionItem>  <AccordionItem key="2" title="Item 2">    Content 2  </AccordionItem></Accordion>

Styling Changes

v2: classNames Prop

<AccordionItem   classNames={{    base: "custom-base",    title: "custom-title",    content: "custom-content"  }}/>

v3: Direct className Props

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

Component Anatomy

The v3 Accordion follows this structure:

Accordion (Root)  └── Accordion.Item      ├── Accordion.Heading      │   └── Accordion.Trigger      │       ├── [Your content: title, subtitle, icons, etc.]      │       └── Accordion.Indicator (optional)      └── Accordion.Panel          └── Accordion.Body              └── [Your content]

Summary

  1. Component Structure: Must use compound components instead of props
  2. State Props: selectedKeys → expandedKeys, onSelectionChange → onExpandedChange
  3. Multiple Selection: selectionMode="multiple" → allowsMultipleExpanded={true}
  4. Item identity: Use id for expanded state; keep React's key for list reconciliation
  5. Variants: Reduced from 4 to 2 variants
  6. Removed Props: Many convenience props removed; use Tailwind CSS classes instead
  7. Content Structure: Title, subtitle, and start content must be manually placed in Trigger
  8. Indicator: Must be explicitly rendered; no automatic indicator