27.5k

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 Componentv3 ComponentNotes
DropdownTriggerDropdown.TriggerSame functionality
DropdownMenuDropdown.MenuWrapped in Dropdown.Popover
DropdownItemDropdown.ItemRequires id instead of key
DropdownSectionDropdown.SectionSame functionality
-Dropdown.PopoverNew 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 - Use className prop directly
    • itemClasses - Use className on individual items
  • DropdownItem:
    • key - Use id instead
    • color - Use variant="danger" for danger items
    • title - Use Label component as child
    • description - Use Description component as child
    • shortcut - Use Kbd component as child
    • startContent - Use icon/components as first child
    • endContent - Use components as last child
    • selectedIcon - Use Dropdown.ItemIndicator component
    • showDivider - Use Separator component between items
    • classNames - Use className prop directly
    • isSelected - Use selectedKeys on Dropdown.Menu
    • isDisabled - Use disabledKeys on Dropdown.Menu

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

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

Breaking Changes 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. key → id: Items use id prop instead of key
  4. textValue Required: All items must have textValue prop
  5. Label Component: Item text must use Label component
  6. Description Component: Use Description component instead of description prop
  7. Kbd Component: Use Kbd component instead of shortcut prop
  8. Icons as Children: Icons go as first child, not startContent prop
  9. Separator Component: Use Separator instead of showDivider prop
  10. ItemIndicator Component: Use Dropdown.ItemIndicator for selection indicators
  11. Variant Instead of Color: Use variant="danger" instead of color="danger"
  12. No Menu Variants: Dropdown.Menu no longer supports variant or color props
  13. ClassNames Removed: Use className props on individual components

Tips for Migration

  1. Start with structure: Convert to compound components first
  2. Add Popover wrapper: Wrap Dropdown.Menu in Dropdown.Popover
  3. Update item props: Change key to id and add textValue
  4. Use Label component: Wrap item text in Label component
  5. Move content to children: Icons, descriptions, shortcuts go as children
  6. Use Separator: Replace showDivider with Separator component
  7. Add ItemIndicator: Add Dropdown.ItemIndicator for selection modes
  8. Update disabled/selected: Use disabledKeys and selectedKeys on menu

Need Help?

For v3 Dropdown features and API:

For community support: