Pro--% off in--d : --h : --m : --s
HeroUI

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 "@heroui/react";

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 "@heroui/react";

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 Componentv3 ComponentNotes
DropdownTriggerDropdown.TriggerSame functionality
DropdownMenuDropdown.MenuWrapped in Dropdown.Popover
DropdownItemDropdown.ItemUse id and textValue; keep key on list items
DropdownSectionDropdown.SectionSame functionality
-Dropdown.PopoverNew 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 Propv3 LocationNotes
variant, color (on DropdownMenu)-Removed (no menu variants)
classNames, itemClasses (on DropdownMenu)-Use className on Menu and items
color (on DropdownItem)Dropdown.ItemUse 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
isSelectedDropdown.MenuUse selectedKeys on Menu
isDisabledDropdown.MenuUse disabledKeys on Menu
trigger (on Dropdown)DropdownStill supported: "press" (default) or "longPress"

5. New Components

  • Dropdown.Popover - Required wrapper around Dropdown.Menu
  • Dropdown.ItemIndicator - For selection indicators (checkmark/dot)
  • Dropdown.SubmenuTrigger - For submenu functionality
  • Dropdown.SubmenuIndicator - For submenu chevron indicator

Migration Examples

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>

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>
import { Icon } from "@iconify/react";
import { Label, Description, Kbd } from "@heroui/react";

{/* With icon */}
<Dropdown.Item id="new" textValue="New file">
  <Icon icon="gravity-ui:square-plus" />
  <Label>New file</Label>
</Dropdown.Item>

{/* With description */}
<Dropdown.Item id="edit" textValue="Edit file">
  <Label>Edit file</Label>
  <Description>Edit the file</Description>
</Dropdown.Item>

{/* With shortcut */}
<Dropdown.Item id="copy" textValue="Copy">
  <Label>Copy</Label>
  <Kbd slot="keyboard" variant="light">
    <Kbd.Abbr keyValue="command" />
    <Kbd.Content>C</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>

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>
import { useState } from "react";

{/* Single selection */}
const [singleSelected, setSingleSelected] = useState(new Set(["text"]));
<Dropdown.Menu
  selectedKeys={singleSelected}
  selectionMode="single"
  onSelectionChange={setSingleSelected}
>
  <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 */}
const [multiSelected, setMultiSelected] = useState(new Set(["bold"]));
<Dropdown.Menu
  selectedKeys={multiSelected}
  selectionMode="multiple"
  onSelectionChange={setMultiSelected}
>
  <Dropdown.Item id="bold" textValue="Bold">
    <Dropdown.ItemIndicator />
    <Label>Bold</Label>
  </Dropdown.Item>
  <Dropdown.Item id="italic" textValue="Italic">
    <Dropdown.ItemIndicator />
    <Label>Italic</Label>
  </Dropdown.Item>
</Dropdown.Menu>

With Keyboard Shortcuts

<DropdownItem key="copy" shortcut="⌘C">
  Copy
</DropdownItem>
import { Label, Kbd } from "@heroui/react";

<Dropdown.Item id="copy" textValue="Copy">
  <Label>Copy</Label>
  <Kbd slot="keyboard" variant="light">
    <Kbd.Abbr keyValue="command" />
    <Kbd.Content>C</Kbd.Content>
  </Kbd>
</Dropdown.Item>

The slot="keyboard" prop on Kbd positions the shortcut at the end of the menu item, replacing the v2 shortcut prop.

With Submenus

{/* v2 did not have built-in submenu support */}
import { Label } from "@heroui/react";

<Dropdown.Menu>
  <Dropdown.Item id="copy-link" textValue="Copy Link">
    <Label>Copy Link</Label>
  </Dropdown.Item>
  <Dropdown.SubmenuTrigger>
    <Dropdown.Item id="share" textValue="Share">
      <Label>Share</Label>
      <Dropdown.SubmenuIndicator />
    </Dropdown.Item>
    <Dropdown.Popover>
      <Dropdown.Menu>
        <Dropdown.Item id="whatsapp" textValue="WhatsApp">
          <Label>WhatsApp</Label>
        </Dropdown.Item>
        <Dropdown.Item id="telegram" textValue="Telegram">
          <Label>Telegram</Label>
        </Dropdown.Item>
      </Dropdown.Menu>
    </Dropdown.Popover>
  </Dropdown.SubmenuTrigger>
</Dropdown.Menu>

Use Dropdown.SubmenuTrigger to wrap an item that opens a nested menu. Place Dropdown.SubmenuIndicator inside the item to render a chevron icon.

Long Press Trigger

<Dropdown>
  <DropdownTrigger>
    <Button>Long press me</Button>
  </DropdownTrigger>
  <DropdownMenu>
    <DropdownItem key="cut">Cut</DropdownItem>
    <DropdownItem key="copy">Copy</DropdownItem>
  </DropdownMenu>
</Dropdown>
<Dropdown trigger="longPress">
  <Button>Long press me</Button>
  <Dropdown.Popover>
    <Dropdown.Menu>
      <Dropdown.Item id="cut" textValue="Cut">
        <Label>Cut</Label>
      </Dropdown.Item>
      <Dropdown.Item id="copy" textValue="Copy">
        <Label>Copy</Label>
      </Dropdown.Item>
    </Dropdown.Menu>
  </Dropdown.Popover>
</Dropdown>

The trigger prop on Dropdown accepts "press" (default) or "longPress" to control how the menu is opened.

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

Summary

  1. Component Structure: Must use compound components (Dropdown.Trigger, Dropdown.Popover, Dropdown.Menu, etc.)
  2. Dropdown.Popover Required: Dropdown.Menu must be wrapped in Dropdown.Popover
  3. Label Component: Item text must use Label component
  4. Description Component: Use Description component instead of description prop
  5. Kbd Component: Use Kbd with slot="keyboard" instead of the shortcut prop — the slot positions the shortcut at the end of the menu item
  6. Icons as Children: Icons go as first child, not startContent prop
  7. Separator Component: Use Separator instead of showDivider prop
  8. ItemIndicator Component: Use Dropdown.ItemIndicator for selection indicators
  9. Variant Instead of Color: Use variant="danger" instead of color="danger"
  10. No Menu Variants: Dropdown.Menu no longer supports variant or color props
  11. ClassNames Removed: Use className props on individual components
  12. Submenu Support: Use Dropdown.SubmenuTrigger to wrap an item that opens a nested menu, and Dropdown.SubmenuIndicator inside the item for a chevron icon
  13. Trigger Prop: Use trigger="longPress" on Dropdown root to open the menu on long press instead of the default press

On this page