@versini/ui-dropdown
v1.3.1
Published
[](https://www.npmjs.com/package/@versini/ui-dropdown)  {
return (
<DropdownMenu
trigger={
<ButtonIcon label="Menu">
<IconMenu />
</ButtonIcon>
}
>
<DropdownMenuItem
label="Profile"
onSelect={() => console.info("Profile")}
/>
<DropdownMenuItem
label="Settings"
onSelect={() => console.info("Settings")}
/>
<DropdownMenuItem
label="Logout"
onSelect={() => console.info("Logout")}
/>
</DropdownMenu>
);
}Examples
Menu with Icons & Selection
import {
DropdownMenu,
DropdownMenuItem,
DropdownMenuSeparator
} from "@versini/ui-dropdown";
import { ButtonIcon } from "@versini/ui-button";
import {
IconMenu,
IconUser,
IconSettings,
IconLogout
} from "@versini/ui-icons";
function AccountMenu() {
const [last, setLast] = useState("");
return (
<DropdownMenu
label="Account options"
trigger={
<ButtonIcon label="Account">
<IconMenu />
</ButtonIcon>
}
onOpenChange={(o) => console.info("open?", o)}
>
<DropdownMenuItem
label="Profile"
icon={<IconUser />}
onSelect={() => setLast("profile")}
/>
<DropdownMenuItem
label="Settings"
icon={<IconSettings />}
onSelect={() => setLast("settings")}
/>
<DropdownMenuSeparator />
<DropdownMenuItem
label="Logout"
icon={<IconLogout />}
onSelect={() => setLast("logout")}
/>
</DropdownMenu>
);
}Raw Custom Item
<DropdownMenu
trigger={
<ButtonIcon label="More">
<IconMenu />
</ButtonIcon>
}
>
<DropdownMenuItem raw ignoreClick>
<div className="p-2 text-xs uppercase tracking-wide text-copy-medium">
Custom Header
</div>
</DropdownMenuItem>
<DropdownMenuItem label="Action" />
</DropdownMenu>Nested Sub-menus
Create hierarchical menus using DropdownMenuSub:
import {
DropdownMenu,
DropdownMenuItem,
DropdownMenuSub,
DropdownMenuGroupLabel
} from "@versini/ui-dropdown";
import { ButtonIcon } from "@versini/ui-button";
import { IconSettings, IconOpenAI, IconAnthropic } from "@versini/ui-icons";
function SettingsMenu() {
const [engine, setEngine] = useState("openai");
return (
<DropdownMenu
trigger={
<ButtonIcon label="Settings">
<IconSettings />
</ButtonIcon>
}
>
<DropdownMenuItem label="Profile" />
<DropdownMenuItem label="Preferences" />
{/* Nested sub-menu with icon */}
<DropdownMenuSub label="AI Settings" icon={<IconSettings />}>
<DropdownMenuGroupLabel icon={<IconSettings />}>
Engines
</DropdownMenuGroupLabel>
<DropdownMenuItem
label="OpenAI"
icon={<IconOpenAI />}
selected={engine === "openai"}
onSelect={() => setEngine("openai")}
/>
<DropdownMenuItem
label="Anthropic"
icon={<IconAnthropic />}
selected={engine === "anthropic"}
onSelect={() => setEngine("anthropic")}
/>
</DropdownMenuSub>
<DropdownMenuItem label="About" />
</DropdownMenu>
);
}Features of nested sub-menus:
- Automatically positioned to the right (or left if no space)
- Visual chevron indicator (
→) shows expandable items - Hover or click to open sub-menus
- Smart positioning adjusts for viewport constraints
- Keyboard navigation works across all levels
- Sibling sub-menus auto-close when opening another
API
DropdownMenu Props
| Prop | Type | Default | Description |
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- | ---------------- | ---------------------------------------------------- |
| trigger | React.ReactNode | - | Element used to open the menu (Button / ButtonIcon). |
| children | React.ReactNode | - | DropdownMenuItem, DropdownMenuSeparator, etc. |
| label | string | "Open menu" | Accessible label for the trigger. |
| defaultPlacement | "bottom" | "bottom-start" | "bottom-end" | "top" | "top-start" | "top-end" | "left" | "left-start" | "right" | etc. | "bottom-start" | Initial preferred placement. |
| mode | "dark" | "light" | "system" | "alt-system" | "system" | Color mode of trigger (when using UI buttons). |
| focusMode | "dark" | "light" | "system" | "alt-system" | "system" | Focus ring thematic mode (when using UI buttons). |
| onOpenChange | (open: boolean) => void | - | Called when menu opens or closes. |
| sideOffset | number | 10 | Offset distance from the trigger element. |
| modal | boolean | true | Whether the dropdown is modal. |
DropdownMenuItem Props
| Prop | Type | Default | Description |
| ------------- | ------------------------ | ----------- | --------------------------------------------- |
| label | string | - | The label to display for the menu item. |
| disabled | boolean | false | Whether the menu item is disabled. |
| icon | React.ReactNode | - | Icon to display on the left of the label. |
| raw | boolean | false | Disable internal styling for custom content. |
| ignoreClick | boolean | false | Prevent menu from closing when item selected. |
| selected | boolean | undefined | Show selected/unselected indicator. |
| onSelect | (event: Event) => void | - | Callback fired when the item is selected. |
DropdownMenuSub Props
| Prop | Type | Default | Description |
| ------------- | ----------------- | ------- | ----------------------------------------- |
| label | string | - | The label for the sub-menu trigger. |
| icon | React.ReactNode | - | Icon to display on the left of the label. |
| children | React.ReactNode | - | Items to render inside sub-menu. |
| disabled | boolean | false | Whether the sub-menu is disabled. |
| sideOffset | number | 2 | Offset from sub-menu trigger. |
| alignOffset | number | -4 | Alignment offset for sub-menu. |
DropdownMenuSeparator Props
Standard React.HTMLAttributes<HTMLDivElement> - use className for custom styling.
DropdownMenuGroupLabel Props
| Prop | Type | Default | Description |
| ----------- | ----------------- | ------- | ----------------------------------------- |
| icon | React.ReactNode | - | Icon to display on the left of the label. |
| children | React.ReactNode | - | The label content. |
| className | string | - | Custom CSS class for styling. |
