Dropdown
Migration guide for Dropdown from HeroUI v2 to v3
Refer to the v3 Dropdown documentation for complete API reference, styling guide, and advanced examples. This guide only focuses on migrating from HeroUI v2.
Structure Changes
In v2, Dropdown used separate components: DropdownTrigger, DropdownMenu, DropdownItem, DropdownSection:
import { Dropdown, DropdownTrigger, DropdownMenu, DropdownItem, Button } from "@lenso/ui";
export default function App() { return ( <Dropdown> <DropdownTrigger> <Button>Open Menu</Button> </DropdownTrigger> <DropdownMenu> <DropdownItem key="new">New file</DropdownItem> <DropdownItem key="copy">Copy link</DropdownItem> </DropdownMenu> </Dropdown> );}In v3, Dropdown uses a compound component pattern with explicit subcomponents:
import { Dropdown, Button, Label } from "@lenso/ui";
export default function App() { return ( <Dropdown> <Button>Open Menu</Button> <Dropdown.Popover> <Dropdown.Menu> <Dropdown.Item id="new" textValue="New file"> <Label>New file</Label> </Dropdown.Item> <Dropdown.Item id="copy" textValue="Copy link"> <Label>Copy link</Label> </Dropdown.Item> </Dropdown.Menu> </Dropdown.Popover> </Dropdown> );}Key Changes
1. Component Structure
v2: Separate components: DropdownTrigger, DropdownMenu, DropdownItem, DropdownSection
v3: Compound components: Dropdown.Trigger, Dropdown.Popover, Dropdown.Menu, Dropdown.Item, Dropdown.Section
2. Component Name Changes
| v2 Component | v3 Component | Notes |
|---|---|---|
DropdownTrigger | Dropdown.Trigger | Same functionality |
DropdownMenu | Dropdown.Menu | Wrapped in Dropdown.Popover |
DropdownItem | Dropdown.Item | Use id and textValue; keep key on list items |
DropdownSection | Dropdown.Section | Same functionality |
| - | Dropdown.Popover | New wrapper component (required) |
3. Item Identification
v2: Item content passed as children; React's key was used for both list reconciliation and item identity (selection, focus).
v3: Item content must use Label component for text. Give each item an id (for state/focus) and textValue (for accessibility when content isn't plain text). Keep React's key on items in lists.
4. Prop Changes
| v2 Prop | v3 Location | Notes |
|---|---|---|
variant, color (on DropdownMenu) | - | Removed (no menu variants) |
classNames, itemClasses (on DropdownMenu) | - | Use className on Menu and items |
color (on DropdownItem) | Dropdown.Item | Use variant="danger" for danger |
title | - | Use Label component as child |
description | - | Use Description component as child |
shortcut | - | Use Kbd component as child |
startContent, endContent | - | Use icon/components as children |
selectedIcon | - | Use Dropdown.ItemIndicator |
showDivider | - | Use Separator between items |
classNames (on DropdownItem) | - | Use className on item |
isSelected | Dropdown.Menu | Use selectedKeys on Menu |
isDisabled | Dropdown.Menu | Use disabledKeys on Menu |
trigger (on Dropdown) | Dropdown | Still supported: "press" (default) or "longPress" |
5. New Components
Dropdown.Popover- Required wrapper aroundDropdown.MenuDropdown.ItemIndicator- For selection indicators (checkmark/dot)Dropdown.SubmenuTrigger- For submenu functionalityDropdown.SubmenuIndicator- For submenu chevron indicator
Migration Examples
With Action Handler
<DropdownMenu onAction={(key) => alert(key)}> <DropdownItem key="new">New file</DropdownItem></DropdownMenu>Item Content
{/* With icon */}<DropdownItem key="new" startContent={<AddNoteIcon />}> New file</DropdownItem>
{/* With description */}<DropdownItem key="edit" description="Edit the file"> Edit file</DropdownItem>
{/* With shortcut */}<DropdownItem key="copy" shortcut="⌘C"> Copy</DropdownItem>Danger Item
<DropdownItem key="delete" className="text-danger" color="danger"> Delete file</DropdownItem>With Sections
<DropdownMenu> <DropdownSection showDivider title="Actions"> <DropdownItem key="new">New file</DropdownItem> <DropdownItem key="edit">Edit file</DropdownItem> </DropdownSection> <DropdownSection title="Danger zone"> <DropdownItem key="delete" color="danger">Delete file</DropdownItem> </DropdownSection></DropdownMenu>Selection
import { useState } from "react";
{/* Single selection */}const [singleSelected, setSingleSelected] = useState(new Set(["text"]));<DropdownMenu selectedKeys={singleSelected} selectionMode="single" onSelectionChange={setSingleSelected}> <DropdownItem key="text">Text</DropdownItem> <DropdownItem key="number">Number</DropdownItem></DropdownMenu>
{/* Multiple selection */}const [multiSelected, setMultiSelected] = useState(new Set(["bold"]));<DropdownMenu selectedKeys={multiSelected} selectionMode="multiple" onSelectionChange={setMultiSelected}> <DropdownItem key="bold">Bold</DropdownItem> <DropdownItem key="italic">Italic</DropdownItem></DropdownMenu>With Keyboard Shortcuts
<DropdownItem key="copy" shortcut="⌘C"> Copy</DropdownItem>With Submenus
{/* v2 did not have built-in submenu support */}Long Press Trigger
<Dropdown> <DropdownTrigger> <Button>Long press me</Button> </DropdownTrigger> <DropdownMenu> <DropdownItem key="cut">Cut</DropdownItem> <DropdownItem key="copy">Copy</DropdownItem> </DropdownMenu></Dropdown>Component Anatomy
The v3 Dropdown follows this structure:
Dropdown (Root) — accepts trigger="press" | "longPress" ├── Dropdown.Trigger (optional, defaults to first child) ├── Dropdown.Popover (required wrapper) │ └── Dropdown.Menu │ ├── Dropdown.Item │ │ ├── Icon (optional, first child) │ │ ├── Label (required for text) │ │ ├── Description (optional) │ │ ├── Kbd slot="keyboard" (optional, for shortcuts) │ │ └── Dropdown.ItemIndicator (optional, for selection) │ ├── Separator (for dividers) │ ├── Dropdown.Section │ │ ├── Header (optional) │ │ └── Dropdown.Item │ └── Dropdown.SubmenuTrigger │ ├── Dropdown.Item │ │ ├── Label │ │ └── Dropdown.SubmenuIndicator (chevron icon) │ └── Dropdown.Popover │ └── Dropdown.Menu │ └── Dropdown.ItemSummary
- Component Structure: Must use compound components (
Dropdown.Trigger,Dropdown.Popover,Dropdown.Menu, etc.) - Dropdown.Popover Required:
Dropdown.Menumust be wrapped inDropdown.Popover - Label Component: Item text must use
Labelcomponent - Description Component: Use
Descriptioncomponent instead ofdescriptionprop - Kbd Component: Use
Kbdwithslot="keyboard"instead of theshortcutprop — the slot positions the shortcut at the end of the menu item - Icons as Children: Icons go as first child, not
startContentprop - Separator Component: Use
Separatorinstead ofshowDividerprop - ItemIndicator Component: Use
Dropdown.ItemIndicatorfor selection indicators - Variant Instead of Color: Use
variant="danger"instead ofcolor="danger" - No Menu Variants:
Dropdown.Menuno longer supportsvariantorcolorprops - ClassNames Removed: Use
classNameprops on individual components - Submenu Support: Use
Dropdown.SubmenuTriggerto wrap an item that opens a nested menu, andDropdown.SubmenuIndicatorinside the item for a chevron icon - Trigger Prop: Use
trigger="longPress"onDropdownroot to open the menu on long press instead of the default press