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.
Overview
The Dropdown component in HeroUI v3 has been redesigned with a compound component pattern, requiring explicit structure with Dropdown.Trigger, Dropdown.Popover, Dropdown.Menu, and Dropdown.Item components.
Structure Changes
v2: Separate Components
In v2, Dropdown used separate components: DropdownTrigger, DropdownMenu, DropdownItem, DropdownSection:
import { Dropdown, DropdownTrigger, DropdownMenu, DropdownItem, Button } from "@heroui/react";
<Dropdown>
<DropdownTrigger>
<Button>Open Menu</Button>
</DropdownTrigger>
<DropdownMenu>
<DropdownItem key="new">New file</DropdownItem>
<DropdownItem key="copy">Copy link</DropdownItem>
</DropdownMenu>
</Dropdown>v3: Compound Component Structure
In v3, Dropdown uses a compound component pattern with explicit subcomponents:
import { Dropdown, Button, Label } from "@heroui/react";
<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 | Requires id instead of key |
DropdownSection | Dropdown.Section | Same functionality |
| - | Dropdown.Popover | New wrapper component (required) |
3. Item Props Changes
v2: Used key prop for item identification
v3: Uses id prop instead of key, and requires textValue prop
v2: Item content passed as children
v3: Item content must use Label component for text
4. Removed Props
The following props are no longer available in v3:
DropdownMenu:variant- Removed (no longer supports visual variants)color- Removed (no longer supports color themes)classNames- UseclassNameprop directlyitemClasses- UseclassNameon individual items
DropdownItem:key- Useidinsteadcolor- Usevariant="danger"for danger itemstitle- UseLabelcomponent as childdescription- UseDescriptioncomponent as childshortcut- UseKbdcomponent as childstartContent- Use icon/components as first childendContent- Use components as last childselectedIcon- UseDropdown.ItemIndicatorcomponentshowDivider- UseSeparatorcomponent between itemsclassNames- UseclassNameprop directlyisSelected- UseselectedKeysonDropdown.MenuisDisabled- UsedisabledKeysonDropdown.Menu
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
Basic Usage
import { Dropdown, DropdownTrigger, DropdownMenu, DropdownItem, Button } from "@heroui/react";
export default function App() {
return (
<Dropdown>
<DropdownTrigger>
<Button variant="bordered">Open Menu</Button>
</DropdownTrigger>
<DropdownMenu aria-label="Static Actions">
<DropdownItem key="new">New file</DropdownItem>
<DropdownItem key="copy">Copy link</DropdownItem>
<DropdownItem key="edit">Edit file</DropdownItem>
<DropdownItem key="delete" className="text-danger" color="danger">
Delete file
</DropdownItem>
</DropdownMenu>
</Dropdown>
);
}import { Dropdown, Button, Label } from "@heroui/react";
export default function App() {
return (
<Dropdown>
<Button variant="secondary">Open Menu</Button>
<Dropdown.Popover>
<Dropdown.Menu onAction={(key) => console.log(`Selected: ${key}`)}>
<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.Item id="edit" textValue="Edit file">
<Label>Edit file</Label>
</Dropdown.Item>
<Dropdown.Item id="delete" textValue="Delete file" variant="danger">
<Label>Delete file</Label>
</Dropdown.Item>
</Dropdown.Menu>
</Dropdown.Popover>
</Dropdown>
);
}With Action Handler
<DropdownMenu onAction={(key) => alert(key)}>
<DropdownItem key="new">New file</DropdownItem>
</DropdownMenu><Dropdown.Menu onAction={(key) => alert(key)}>
<Dropdown.Item id="new" textValue="New file">
<Label>New file</Label>
</Dropdown.Item>
</Dropdown.Menu>With Icons
<DropdownItem
key="new"
startContent={<AddNoteIcon />}
>
New file
</DropdownItem>import { Icon } from "@iconify/react";
<Dropdown.Item id="new" textValue="New file">
<Icon icon="gravity-ui:square-plus" />
<Label>New file</Label>
</Dropdown.Item>With Description
<DropdownItem
key="new"
description="Create a new file"
>
New file
</DropdownItem>import { Label, Description } from "@heroui/react";
<Dropdown.Item id="new" textValue="New file">
<Label>New file</Label>
<Description>Create a new file</Description>
</Dropdown.Item>With Shortcut
<DropdownItem
key="new"
shortcut="⌘N"
>
New file
</DropdownItem>import { Label, Kbd } from "@heroui/react";
<Dropdown.Item id="new" textValue="New file">
<Label>New file</Label>
<Kbd slot="keyboard" variant="light">
<Kbd.Abbr keyValue="command" />
<Kbd.Content>N</Kbd.Content>
</Kbd>
</Dropdown.Item>Danger Item
<DropdownItem
key="delete"
className="text-danger"
color="danger"
>
Delete file
</DropdownItem><Dropdown.Item id="delete" textValue="Delete file" variant="danger">
<Label>Delete file</Label>
</Dropdown.Item>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>import { Header, Separator } from "@heroui/react";
<Dropdown.Menu>
<Dropdown.Section>
<Header>Actions</Header>
<Dropdown.Item id="new" textValue="New file">
<Label>New file</Label>
</Dropdown.Item>
<Dropdown.Item id="edit" textValue="Edit file">
<Label>Edit file</Label>
</Dropdown.Item>
</Dropdown.Section>
<Separator />
<Dropdown.Section>
<Header>Danger zone</Header>
<Dropdown.Item id="delete" textValue="Delete file" variant="danger">
<Label>Delete file</Label>
</Dropdown.Item>
</Dropdown.Section>
</Dropdown.Menu>Single Selection
import { useState } from "react";
const [selectedKeys, setSelectedKeys] = useState(new Set(["text"]));
<DropdownMenu
selectedKeys={selectedKeys}
selectionMode="single"
onSelectionChange={setSelectedKeys}
>
<DropdownItem key="text">Text</DropdownItem>
<DropdownItem key="number">Number</DropdownItem>
</DropdownMenu>import { useState } from "react";
const [selectedKeys, setSelectedKeys] = useState(new Set(["text"]));
<Dropdown.Menu
selectedKeys={selectedKeys}
selectionMode="single"
onSelectionChange={setSelectedKeys}
>
<Dropdown.Item id="text" textValue="Text">
<Dropdown.ItemIndicator />
<Label>Text</Label>
</Dropdown.Item>
<Dropdown.Item id="number" textValue="Number">
<Dropdown.ItemIndicator />
<Label>Number</Label>
</Dropdown.Item>
</Dropdown.Menu>Multiple Selection
import { useState } from "react";
const [selectedKeys, setSelectedKeys] = useState(new Set(["bold"]));
<DropdownMenu
selectedKeys={selectedKeys}
selectionMode="multiple"
onSelectionChange={setSelectedKeys}
>
<DropdownItem key="bold">Bold</DropdownItem>
<DropdownItem key="italic">Italic</DropdownItem>
</DropdownMenu>import { useState } from "react";
const [selectedKeys, setSelectedKeys] = useState(new Set(["bold"]));
<Dropdown.Menu
selectedKeys={selectedKeys}
selectionMode="multiple"
onSelectionChange={setSelectedKeys}
>
<Dropdown.Item id="bold" textValue="Bold">
<Label>Bold</Label>
<Dropdown.ItemIndicator />
</Dropdown.Item>
<Dropdown.Item id="italic" textValue="Italic">
<Label>Italic</Label>
<Dropdown.ItemIndicator />
</Dropdown.Item>
</Dropdown.Menu>Disabled Items
<DropdownMenu disabledKeys={["delete"]}>
<DropdownItem key="new">New file</DropdownItem>
<DropdownItem key="delete">Delete file</DropdownItem>
</DropdownMenu><Dropdown.Menu disabledKeys={["delete"]}>
<Dropdown.Item id="new" textValue="New file">
<Label>New file</Label>
</Dropdown.Item>
<Dropdown.Item id="delete" textValue="Delete file">
<Label>Delete file</Label>
</Dropdown.Item>
</Dropdown.Menu>Custom Trigger
<Dropdown>
<DropdownTrigger>
<CustomButton>Menu</CustomButton>
</DropdownTrigger>
<DropdownMenu>
<DropdownItem key="new">New file</DropdownItem>
</DropdownMenu>
</Dropdown><Dropdown>
<Dropdown.Trigger>
<CustomButton>Menu</CustomButton>
</Dropdown.Trigger>
<Dropdown.Popover>
<Dropdown.Menu>
<Dropdown.Item id="new" textValue="New file">
<Label>New file</Label>
</Dropdown.Item>
</Dropdown.Menu>
</Dropdown.Popover>
</Dropdown>Controlled Open State
import { useState } from "react";
const [isOpen, setIsOpen] = useState(false);
<Dropdown
isOpen={isOpen}
onOpenChange={setIsOpen}
>
<DropdownTrigger>
<Button>Menu</Button>
</DropdownTrigger>
<DropdownMenu>
<DropdownItem key="new">New file</DropdownItem>
</DropdownMenu>
</Dropdown>import { useState } from "react";
const [isOpen, setIsOpen] = useState(false);
<Dropdown
isOpen={isOpen}
onOpenChange={setIsOpen}
>
<Button>Menu</Button>
<Dropdown.Popover>
<Dropdown.Menu>
<Dropdown.Item id="new" textValue="New file">
<Label>New file</Label>
</Dropdown.Item>
</Dropdown.Menu>
</Dropdown.Popover>
</Dropdown>Component Anatomy
The v3 Dropdown follows this structure:
Dropdown (Root)
├── 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 (optional, for shortcuts)
│ │ └── Dropdown.ItemIndicator (optional, for selection)
│ ├── Separator (for dividers)
│ └── Dropdown.Section
│ ├── Header (optional)
│ └── Dropdown.ItemBreaking Changes Summary
- Component Structure: Must use compound components (
Dropdown.Trigger,Dropdown.Popover,Dropdown.Menu, etc.) - Dropdown.Popover Required:
Dropdown.Menumust be wrapped inDropdown.Popover - key → id: Items use
idprop instead ofkey - textValue Required: All items must have
textValueprop - Label Component: Item text must use
Labelcomponent - Description Component: Use
Descriptioncomponent instead ofdescriptionprop - Kbd Component: Use
Kbdcomponent instead ofshortcutprop - 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
Tips for Migration
- Start with structure: Convert to compound components first
- Add Popover wrapper: Wrap
Dropdown.MenuinDropdown.Popover - Update item props: Change
keytoidand addtextValue - Use Label component: Wrap item text in
Labelcomponent - Move content to children: Icons, descriptions, shortcuts go as children
- Use Separator: Replace
showDividerwithSeparatorcomponent - Add ItemIndicator: Add
Dropdown.ItemIndicatorfor selection modes - Update disabled/selected: Use
disabledKeysandselectedKeyson menu
Need Help?
For v3 Dropdown features and API:
- See the API Reference
- Check interactive examples
For community support: