Dropdown
A Dropdown is an interactive overlay menu anchored to a trigger element. It is built on top of the Popover component and is designed for action menus, navigation lists, and nested submenus.
Key Characteristics
- Composable: Build menus declaratively using
Dropdown.Menu,Dropdown.Section,Dropdown.Item,Dropdown.Divider,Dropdown.Header, andDropdown.Footer. - Nestable: Create nested submenus with
isNested- opens on hover with a caret indicator. - Polymorphic Items: Render items as any element (
div,a,Link, etc.) using theasprop. - Accessible: Full keyboard navigation, focus management, and proper ARIA attributes built in.
Import
There are 7 dropdown-related components:
Dropdown: The main dropdown component.DropdownTrigger: The element that toggles the dropdown.DropdownMenu: The container for dropdown content.DropdownItem: A clickable menu item.DropdownSection: A group of items with an optional title.DropdownDivider: A visual separator between sections.DropdownHeader/DropdownFooter: Non-clickable top/bottom slots.
import {
Dropdown,
DropdownTrigger,
DropdownMenu,
DropdownItem,
DropdownSection,
DropdownDivider,
} from '@andrejground/lab';
Compound pattern is also supported:
Dropdown.TriggerDropdown.MenuDropdown.ItemDropdown.SectionDropdown.DividerDropdown.HeaderDropdown.Footer
Usage
- Preview
- Code
import { Dropdown } from '@andrejground/lab';
export default function App() {
return (
<Dropdown>
<Dropdown.Trigger>
<button>Open Menu</button>
</Dropdown.Trigger>
<Dropdown.Menu>
<Dropdown.Item onClick={() => alert('New file')}>
New file
</Dropdown.Item>
<Dropdown.Item onClick={() => alert('Copy link')}>
Copy link
</Dropdown.Item>
<Dropdown.Item onClick={() => alert('Edit')}>
Edit
</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
);
}
Sections
Use Dropdown.Section to group items with titles, and Dropdown.Divider to visually separate groups.
- Preview
- Code
import { Dropdown } from '@andrejground/lab';
export default function App() {
return (
<Dropdown>
<Dropdown.Trigger>
<button>Open Menu</button>
</Dropdown.Trigger>
<Dropdown.Menu>
<Dropdown.Section title="Actions">
<Dropdown.Item description="Create a new file">
New file
</Dropdown.Item>
<Dropdown.Item description="Copy the file link">
Copy link
</Dropdown.Item>
</Dropdown.Section>
<Dropdown.Divider />
<Dropdown.Section title="Danger zone">
<Dropdown.Item description="Permanently delete this file">
Delete
</Dropdown.Item>
</Dropdown.Section>
</Dropdown.Menu>
</Dropdown>
);
}
Nested
Use isNested to create submenus. Nested dropdowns open on hover (and/or click) and display a caret indicator.
Keyboard navigation is fully supported between parent and child menus. Use Enter to open a nested menu, and Esc to close it.
- Preview
- Code
import { Dropdown } from '@andrejground/lab';
export default function App() {
return (
<Dropdown>
<Dropdown.Trigger>
<button>Actions</button>
</Dropdown.Trigger>
<Dropdown.Menu>
<Dropdown.Item>New file</Dropdown.Item>
<Dropdown.Item>Copy link</Dropdown.Item>
<Dropdown isNested>
<Dropdown.Trigger>
<Dropdown.Item shouldCloseOnSelection={false}>
Export
</Dropdown.Item>
</Dropdown.Trigger>
<Dropdown.Menu>
<Dropdown.Item>JSON</Dropdown.Item>
<Dropdown.Item>CSV</Dropdown.Item>
<Dropdown.Item>PDF</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
</Dropdown.Menu>
</Dropdown>
);
}
Disabled Items
Use the disabled prop on Dropdown.Item to prevent interaction.
- Preview
- Code
import { Dropdown } from '@andrejground/lab';
export default function App() {
return (
<Dropdown>
<Dropdown.Trigger>
<button>Open Menu</button>
</Dropdown.Trigger>
<Dropdown.Menu>
<Dropdown.Item>New file</Dropdown.Item>
<Dropdown.Item disabled>Copy link (disabled)</Dropdown.Item>
<Dropdown.Item>Edit</Dropdown.Item>
<Dropdown.Item disabled>Delete (disabled)</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
);
}
Polymorphic Items
Use the as prop to render items as different elements - links, anchors, or any other component.
- Preview
- Code
import { Dropdown } from '@andrejground/lab';
export default function App() {
return (
<Dropdown>
<Dropdown.Trigger>
<button>Navigation</button>
</Dropdown.Trigger>
<Dropdown.Menu>
<Dropdown.Item as="a" href="/blog" description="Visit the blog">
Blog (Link)
</Dropdown.Item>
<Dropdown.Item as="a" href="https://github.com" target="_blank" description="Open GitHub">
GitHub (anchor)
</Dropdown.Item>
<Dropdown.Item onClick={() => alert('Clicked!')} description="Triggers an action">
Action (div)
</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
);
}
Controlled
- Preview
- Code
falseimport React from 'react';
import { Dropdown } from '@andrejground/lab';
export default function App() {
const [isOpen, setIsOpen] = React.useState(false);
return (
<>
<Dropdown isOpen={isOpen} onOpenChange={setIsOpen}>
<Dropdown.Trigger>
<button>Open Menu</button>
</Dropdown.Trigger>
<Dropdown.Menu>
<Dropdown.Item>New file</Dropdown.Item>
<Dropdown.Item>Copy link</Dropdown.Item>
<Dropdown.Item>Edit</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
<br />
<div>
Open: <code>{`${isOpen}`}</code>
</div>
</>
);
}