Skip to content

Dropdown

Dropdown menu component for displaying actions and options.

import { Dropdown } from 'asterui'

Basic Dropdown

Simple dropdown menu with items using compound pattern.

import { Dropdown, Button } from 'asterui'

function App() {
  return (
      <Dropdown>
        <Dropdown.Trigger>
          <Button color="primary">Actions</Button>
        </Dropdown.Trigger>
        <Dropdown.Menu>
          <Dropdown.Item>Edit</Dropdown.Item>
          <Dropdown.Item>Duplicate</Dropdown.Item>
          <Dropdown.Item>Delete</Dropdown.Item>
        </Dropdown.Menu>
      </Dropdown>
    )
}

export default App

Data-Driven (menu prop)

Define menu items via the menu prop instead of compound components.

import { Dropdown, Button } from 'asterui'
import { PencilIcon, DocumentDuplicateIcon, TrashIcon } from '@aster-ui/icons'

function App() {
  const menuItems = [
    { key: 'edit', label: 'Edit', icon: <PencilIcon size="sm" /> },
    { key: 'duplicate', label: 'Duplicate', icon: <DocumentDuplicateIcon size="sm" /> },
    { type: 'divider' as const },
    { key: 'delete', label: 'Delete', danger: true, icon: <TrashIcon size="sm" /> },
  ]
  
  return (
      <Dropdown menu={{ items: menuItems }}>
        <Dropdown.Trigger>
          <Button color="primary">Data-Driven</Button>
        </Dropdown.Trigger>
      </Dropdown>
    )
}

export default App

Placement

Dropdown menu can be positioned in different directions.

import { Dropdown, Button, Space } from 'asterui'

function App() {
  return (
      <Space direction="horizontal" size="sm" wrap>
        <Dropdown placement="top">
          <Dropdown.Trigger>
            <Button>Top</Button>
          </Dropdown.Trigger>
          <Dropdown.Menu>
            <Dropdown.Item>Option 1</Dropdown.Item>
            <Dropdown.Item>Option 2</Dropdown.Item>
            <Dropdown.Item>Option 3</Dropdown.Item>
          </Dropdown.Menu>
        </Dropdown>
  
        <Dropdown placement="bottom">
          <Dropdown.Trigger>
            <Button>Bottom</Button>
          </Dropdown.Trigger>
          <Dropdown.Menu>
            <Dropdown.Item>Option 1</Dropdown.Item>
            <Dropdown.Item>Option 2</Dropdown.Item>
            <Dropdown.Item>Option 3</Dropdown.Item>
          </Dropdown.Menu>
        </Dropdown>
  
        <Dropdown placement="left">
          <Dropdown.Trigger>
            <Button>Left</Button>
          </Dropdown.Trigger>
          <Dropdown.Menu>
            <Dropdown.Item>Option 1</Dropdown.Item>
            <Dropdown.Item>Option 2</Dropdown.Item>
            <Dropdown.Item>Option 3</Dropdown.Item>
          </Dropdown.Menu>
        </Dropdown>
  
        <Dropdown placement="right">
          <Dropdown.Trigger>
            <Button>Right</Button>
          </Dropdown.Trigger>
          <Dropdown.Menu>
            <Dropdown.Item>Option 1</Dropdown.Item>
            <Dropdown.Item>Option 2</Dropdown.Item>
            <Dropdown.Item>Option 3</Dropdown.Item>
          </Dropdown.Menu>
        </Dropdown>
      </Space>
    )
}

export default App

Trigger Modes

Control how the dropdown opens: click, hover, or both.

import { Dropdown, Button, Space } from 'asterui'

function App() {
  return (
      <Space direction="horizontal" size="sm" wrap>
        <Dropdown trigger={['click']}>
          <Dropdown.Trigger>
            <Button>Click</Button>
          </Dropdown.Trigger>
          <Dropdown.Menu>
            <Dropdown.Item>Option 1</Dropdown.Item>
            <Dropdown.Item>Option 2</Dropdown.Item>
          </Dropdown.Menu>
        </Dropdown>
  
        <Dropdown trigger={['hover']}>
          <Dropdown.Trigger>
            <Button>Hover</Button>
          </Dropdown.Trigger>
          <Dropdown.Menu>
            <Dropdown.Item>Option 1</Dropdown.Item>
            <Dropdown.Item>Option 2</Dropdown.Item>
          </Dropdown.Menu>
        </Dropdown>
      </Space>
    )
}

export default App

With Icons

Menu items with icons for better visual context.

import { Dropdown, Button } from 'asterui'
import { PencilIcon, DocumentDuplicateIcon, TrashIcon } from '@aster-ui/icons'

function App() {
  return (
      <Dropdown>
        <Dropdown.Trigger>
          <Button color="primary">Options</Button>
        </Dropdown.Trigger>
        <Dropdown.Menu>
          <Dropdown.Item icon={<PencilIcon size="sm" />}>Edit</Dropdown.Item>
          <Dropdown.Item icon={<DocumentDuplicateIcon size="sm" />}>Duplicate</Dropdown.Item>
          <Dropdown.Item icon={<TrashIcon size="sm" />}>Delete</Dropdown.Item>
        </Dropdown.Menu>
      </Dropdown>
    )
}

export default App

With Divider

Separate menu sections with dividers.

import { Dropdown, Button } from 'asterui'

function App() {
  return (
      <Dropdown>
        <Dropdown.Trigger>
          <Button>Account</Button>
        </Dropdown.Trigger>
        <Dropdown.Menu>
          <Dropdown.Item>Profile</Dropdown.Item>
          <Dropdown.Item>Settings</Dropdown.Item>
          <Dropdown.Divider />
          <Dropdown.Item>Help</Dropdown.Item>
          <Dropdown.Item danger>Sign out</Dropdown.Item>
        </Dropdown.Menu>
      </Dropdown>
    )
}

export default App

Item States

Items can be active, disabled, or styled as danger.

import { Dropdown, Button } from 'asterui'

function App() {
  return (
      <Dropdown>
        <Dropdown.Trigger>
          <Button>States</Button>
        </Dropdown.Trigger>
        <Dropdown.Menu>
          <Dropdown.Item active>Active Item</Dropdown.Item>
          <Dropdown.Item>Normal Item</Dropdown.Item>
          <Dropdown.Item disabled>Disabled Item</Dropdown.Item>
          <Dropdown.Divider />
          <Dropdown.Item danger>Delete Account</Dropdown.Item>
        </Dropdown.Menu>
      </Dropdown>
    )
}

export default App

Alignment

Align the dropdown menu to start, center, or end.

import { Dropdown, Button, Space } from 'asterui'

function App() {
  return (
      <Space direction="horizontal" size="sm" wrap>
        <Dropdown placement="bottomLeft">
          <Dropdown.Trigger>
            <Button>Bottom Left</Button>
          </Dropdown.Trigger>
          <Dropdown.Menu>
            <Dropdown.Item>Option 1</Dropdown.Item>
            <Dropdown.Item>Option 2</Dropdown.Item>
          </Dropdown.Menu>
        </Dropdown>
  
        <Dropdown placement="bottom">
          <Dropdown.Trigger>
            <Button>Bottom Center</Button>
          </Dropdown.Trigger>
          <Dropdown.Menu>
            <Dropdown.Item>Option 1</Dropdown.Item>
            <Dropdown.Item>Option 2</Dropdown.Item>
          </Dropdown.Menu>
        </Dropdown>
  
        <Dropdown placement="bottomRight">
          <Dropdown.Trigger>
            <Button>Bottom Right</Button>
          </Dropdown.Trigger>
          <Dropdown.Menu>
            <Dropdown.Item>Option 1</Dropdown.Item>
            <Dropdown.Item>Option 2</Dropdown.Item>
          </Dropdown.Menu>
        </Dropdown>
      </Space>
    )
}

export default App

Context Menu

Open dropdown on right-click using contextMenu trigger.

import { Dropdown, Typography } from 'asterui'
import { PencilIcon, DocumentDuplicateIcon, TrashIcon } from '@aster-ui/icons'

function App() {
  return (
      <Dropdown trigger={['contextMenu']}>
        <Dropdown.Trigger>
          <div className="p-8 border-2 border-dashed border-base-300 rounded-lg text-center cursor-context-menu">
            <Typography.Text type="secondary">Right-click here</Typography.Text>
          </div>
        </Dropdown.Trigger>
        <Dropdown.Menu>
          <Dropdown.Item icon={<PencilIcon size="sm" />}>Edit</Dropdown.Item>
          <Dropdown.Item icon={<DocumentDuplicateIcon size="sm" />}>Copy</Dropdown.Item>
          <Dropdown.Divider />
          <Dropdown.Item icon={<TrashIcon size="sm" />} danger>Delete</Dropdown.Item>
        </Dropdown.Menu>
      </Dropdown>
    )
}

export default App

With Submenu

Nested submenus for hierarchical options.

import { Dropdown, Button } from 'asterui'
import { PencilIcon, FolderIcon, Cog6ToothIcon } from '@aster-ui/icons'

function App() {
  return (
      <Dropdown>
        <Dropdown.Trigger>
          <Button>With Submenu</Button>
        </Dropdown.Trigger>
        <Dropdown.Menu>
          <Dropdown.Item icon={<PencilIcon size="sm" />}>Edit</Dropdown.Item>
          <Dropdown.SubMenu title="More Options" icon={<FolderIcon size="sm" />}>
            <Dropdown.Item>Option A</Dropdown.Item>
            <Dropdown.Item>Option B</Dropdown.Item>
            <Dropdown.Item>Option C</Dropdown.Item>
          </Dropdown.SubMenu>
          <Dropdown.SubMenu title="Settings" icon={<Cog6ToothIcon size="sm" />}>
            <Dropdown.Item>Preferences</Dropdown.Item>
            <Dropdown.Item>Account</Dropdown.Item>
          </Dropdown.SubMenu>
          <Dropdown.Divider />
          <Dropdown.Item danger>Delete</Dropdown.Item>
        </Dropdown.Menu>
      </Dropdown>
    )
}

export default App

Compact Button with Dropdown

Combine a button with a dropdown using Join for a compact split-button appearance. The Button is automatically treated as the trigger without needing Dropdown.Trigger wrapper. Opens on hover automatically.

import { Dropdown, Button, Join } from 'asterui'
import { UserIcon } from '@aster-ui/icons'

function App() {
  const menuItems = [
    { key: 'profile', label: 'Profile' },
    { key: 'settings', label: 'Settings' },
    { type: 'divider' as const },
    { key: 'logout', label: 'Sign out', danger: true },
  ]
  
  return (
      <Join>
        <Button color="primary">Actions</Button>
        <Dropdown menu={{ items: menuItems }} placement="bottomRight">
          <Button color="primary" icon={<UserIcon />} />
        </Dropdown>
      </Join>
    )
}

export default App

Compact Icon Dropdown

Icon-only dropdown button joined with another button using the simplified API (no Dropdown.Trigger wrapper needed). Opens on hover for quick access.

import { Dropdown, Button, Join } from 'asterui'
import { EllipsisVerticalIcon } from '@aster-ui/icons'

function App() {
  return (
      <Join>
        <Button>Save</Button>
        <Dropdown placement="bottomRight">
          <Button icon={<EllipsisVerticalIcon />} />
          <Dropdown.Menu>
            <Dropdown.Item>Save and Close</Dropdown.Item>
            <Dropdown.Item>Save as Draft</Dropdown.Item>
            <Dropdown.Divider />
            <Dropdown.Item>Discard</Dropdown.Item>
          </Dropdown.Menu>
        </Dropdown>
      </Join>
    )
}

export default App
PropertyDescriptionTypeDefault
childrenTrigger element and dropdown content (compound pattern)React.ReactNode-
menuMenu configuration (data-driven pattern){ items: DropdownMenuItemType[], onClick?: (info: { key: string; keyPath: string[] }) => void }-
triggerTrigger mode(s) for dropdown('click' | 'hover' | 'contextMenu')[]['hover']
placementPlacement of dropdown menu'top' | 'topLeft' | 'topRight' | 'bottom' | 'bottomLeft' | 'bottomRight' | 'left' | 'right''bottomLeft'
openControlled open stateboolean-
onOpenChangeCallback when open state changes(open: boolean, info?: { source }) => void-
disabledDisable the dropdownbooleanfalse
arrowShow arrow pointing to triggerboolean | { pointAtCenter?: boolean }false
autoAdjustOverflowWhether to adjust dropdown placement automatically when dropdown is off screenbooleantrue
mouseEnterDelayDelay before showing on hover (seconds)number0.15
mouseLeaveDelayDelay before hiding on mouse leave (seconds)number0.1
destroyOnHiddenDestroy dropdown when hiddenbooleanfalse
popupRenderCustomize popup content(menu: ReactNode) => ReactNode-
data-testidTest ID prefix for child elementsstring-
classNameAdditional CSS classesstring-
PropertyDescriptionTypeDefault
keyUnique key for the item (React key prop)string-
childrenItem contentReact.ReactNode-
labelItem label (alternative to children)React.ReactNode-
iconIcon to display before labelReact.ReactNode-
onClickClick handler() => void-
activeActive/selected statebooleanfalse
disabledDisable the itembooleanfalse
dangerDanger/destructive stylingbooleanfalse
classNameAdditional CSS classesstring-
PropertyDescriptionTypeDefault
keyUnique key for the submenu (React key prop)string-
childrenSubmenu itemsReact.ReactNode-
titleSubmenu title/labelReact.ReactNode-
iconIcon to display before titleReact.ReactNode-
disabledDisable the submenubooleanfalse
  • Keyboard navigation with Enter, Space, and Escape keys
  • Arrow keys to navigate through menu items
  • Proper ARIA attributes for screen readers
  • Focus management when opening and closing the menu
  • Click outside to close behavior