[](https://www.npmjs.com/package/@versini/ui-dropdown) 
!npm package minimized gzipped size>)
> Accessible and flexible React dropdown menu components built with TypeScript, TailwindCSS, and Radix UI primitives.
The DropdownMenu package provides dropdown menus with full keyboard navigation, focus management, theming for triggers, and composable items / separators.
- Features
- Installation
- Usage
- Examples
- API
- ๐ Composable: DropdownMenu, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuGroupLabel, DropdownMenuSub
- ๐ Nested Sub-menus: Support for multi-level menu hierarchies with automatic positioning
- โฟ Accessible: Built with Radix UI primitives & ARIA roles for robust a11y
- โจ๏ธ Keyboard Support: Arrow navigation, typeahead matching, ESC / click outside close
- ๐จ Theme & Focus Modes: Trigger inherits color + separate focus styling
- ๐งญ Smart Positioning: Auto flip / shift to remain within viewport
- ๐งช Type Safe: Strongly typed props with TypeScript
``bash`
npm install @versini/ui-dropdown
> Note: This component requires TailwindCSS and the @versini/ui-styles plugin for proper styling. See the installation documentation for complete setup instructions.
`tsx
import { DropdownMenu, DropdownMenuItem } from "@versini/ui-dropdown";
import { ButtonIcon } from "@versini/ui-button";
import { IconMenu } from "@versini/ui-icons";
function App() {
return (
}
>
onSelect={() => console.info("Profile")}
/>
onSelect={() => console.info("Settings")}
/>
onSelect={() => console.info("Logout")}
/>
);
}
`
`tsx
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 (
trigger={
}
onOpenChange={(o) => console.info("open?", o)}
>
icon={
onSelect={() => setLast("profile")}
/>
icon={
onSelect={() => setLast("settings")}
/>
icon={
onSelect={() => setLast("logout")}
/>
);
}
`
`tsx`
}
>
Custom Header
Create hierarchical menus using DropdownMenuSub:
`tsx
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 (
}
>
{/ Nested sub-menu with icon /}
Engines
icon={
selected={engine === "openai"}
onSelect={() => setEngine("openai")}
/>
icon={
selected={engine === "anthropic"}
onSelect={() => setEngine("anthropic")}
/>
);
}
`
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
| 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. |
| 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. |
| 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. |
Standard React.HTMLAttributes - use className for custom styling.
| 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. |