Dropdown
Dropdown menu component for displaying actions and options.
Import
Section titled “Import”import { Dropdown } from 'asterui'Examples
Section titled “Examples”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 Dropdown
Section titled “Dropdown”| Property | Description | Type | Default |
|---|---|---|---|
children | Trigger element and dropdown content (compound pattern) | React.ReactNode | - |
menu | Menu configuration (data-driven pattern) | { items: DropdownMenuItemType[], onClick?: (info: { key: string; keyPath: string[] }) => void } | - |
trigger | Trigger mode(s) for dropdown | ('click' | 'hover' | 'contextMenu')[] | ['hover'] |
placement | Placement of dropdown menu | 'top' | 'topLeft' | 'topRight' | 'bottom' | 'bottomLeft' | 'bottomRight' | 'left' | 'right' | 'bottomLeft' |
open | Controlled open state | boolean | - |
onOpenChange | Callback when open state changes | (open: boolean, info?: { source }) => void | - |
disabled | Disable the dropdown | boolean | false |
arrow | Show arrow pointing to trigger | boolean | { pointAtCenter?: boolean } | false |
autoAdjustOverflow | Whether to adjust dropdown placement automatically when dropdown is off screen | boolean | true |
mouseEnterDelay | Delay before showing on hover (seconds) | number | 0.15 |
mouseLeaveDelay | Delay before hiding on mouse leave (seconds) | number | 0.1 |
destroyOnHidden | Destroy dropdown when hidden | boolean | false |
popupRender | Customize popup content | (menu: ReactNode) => ReactNode | - |
data-testid | Test ID prefix for child elements | string | - |
className | Additional CSS classes | string | - |
Dropdown.Item
Section titled “Dropdown.Item”| Property | Description | Type | Default |
|---|---|---|---|
key | Unique key for the item (React key prop) | string | - |
children | Item content | React.ReactNode | - |
label | Item label (alternative to children) | React.ReactNode | - |
icon | Icon to display before label | React.ReactNode | - |
onClick | Click handler | () => void | - |
active | Active/selected state | boolean | false |
disabled | Disable the item | boolean | false |
danger | Danger/destructive styling | boolean | false |
className | Additional CSS classes | string | - |
Dropdown.SubMenu
Section titled “Dropdown.SubMenu”| Property | Description | Type | Default |
|---|---|---|---|
key | Unique key for the submenu (React key prop) | string | - |
children | Submenu items | React.ReactNode | - |
title | Submenu title/label | React.ReactNode | - |
icon | Icon to display before title | React.ReactNode | - |
disabled | Disable the submenu | boolean | false |
Accessibility
Section titled “Accessibility”- Keyboard navigation with
Enter,Space, andEscapekeys - 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