@versini/ui-menu
v7.0.4
Published
[](https://www.npmjs.com/package/@versini/ui-menu)  {
return (
<Menu
trigger={
<ButtonIcon label="Menu">
<IconMenu />
</ButtonIcon>
}
>
<MenuGroup>
<MenuItem label="Profile" onSelect={() => console.info("Profile")} />
<MenuItem label="Settings" onSelect={() => console.info("Settings")} />
<MenuItem label="Logout" onSelect={() => console.info("Logout")} />
</MenuGroup>
</Menu>
);
}Examples
Menu with Icons & Selection
import { Menu, MenuGroup, MenuItem, MenuSeparator } from "@versini/ui-menu";
import { ButtonIcon } from "@versini/ui-button";
import {
IconMenu,
IconUser,
IconSettings,
IconLogout
} from "@versini/ui-icons";
function AccountMenu() {
const [last, setLast] = useState("");
return (
<Menu
label="Account options"
trigger={
<ButtonIcon label="Account">
<IconMenu />
</ButtonIcon>
}
onOpenChange={(o) => console.info("open?", o)}
>
<MenuGroup>
<MenuItem
label="Profile"
icon={<IconUser />}
onSelect={() => setLast("profile")}
/>
<MenuItem
label="Settings"
icon={<IconSettings />}
onSelect={() => setLast("settings")}
/>
<MenuSeparator />
<MenuItem
label="Logout"
icon={<IconLogout />}
onSelect={() => setLast("logout")}
/>
</MenuGroup>
</Menu>
);
}Grouped Menu Items
Use MenuGroup to visually group related items with an optional label:
import { Menu, MenuGroup, MenuItem } from "@versini/ui-menu";
import { ButtonIcon } from "@versini/ui-button";
import { IconSettings, IconOpenAI, IconAnthropic } from "@versini/ui-icons";
function SettingsMenu() {
const [selected, setSelected] = useState(0);
return (
<Menu
trigger={
<ButtonIcon label="Settings">
<IconSettings />
</ButtonIcon>
}
>
<MenuGroup label="Engines">
<MenuItem
label="OpenAI"
icon={<IconOpenAI />}
selected={selected === 1}
onClick={() => setSelected(1)}
/>
<MenuItem
label="Anthropic"
icon={<IconAnthropic />}
selected={selected === 2}
onClick={() => setSelected(2)}
/>
</MenuGroup>
<MenuGroup label="Personas" className="mt-2">
<MenuItem label="Diggidy" selected={selected === 3} />
<MenuItem label="French Teacher" selected={selected === 4} />
</MenuGroup>
<MenuGroup className="mt-2">
<MenuItem label="About" />
</MenuGroup>
</Menu>
);
}Menu with a Label
Use MenuLabel to add a non-interactive heading inside a menu:
import { Menu, MenuGroup, MenuItem, MenuLabel } from "@versini/ui-menu";
import { ButtonIcon } from "@versini/ui-button";
import { IconBookSparkles, IconMagic, IconProofread } from "@versini/ui-icons";
function PromptsMenu() {
return (
<Menu
trigger={
<ButtonIcon label="Prompts">
<IconBookSparkles />
</ButtonIcon>
}
>
<MenuGroup>
<MenuLabel>Prompts</MenuLabel>
<MenuItem label="Summarize..." icon={<IconMagic />} />
<MenuItem label="Proofread..." icon={<IconProofread />} />
</MenuGroup>
</Menu>
);
}Nested Sub-menus
Create hierarchical menus using MenuSub. Groups work inside sub-menus too:
import {
Menu,
MenuItem,
MenuSub,
MenuGroup,
MenuSeparator
} from "@versini/ui-menu";
import { ButtonIcon } from "@versini/ui-button";
import {
IconSettings,
IconOpenAI,
IconAnthropic,
IconStarInCircle,
IconFrenchFlag
} from "@versini/ui-icons";
function SettingsMenu() {
const [model, setModel] = useState("openai");
const [persona, setPersona] = useState("diggidy");
return (
<Menu
trigger={
<ButtonIcon label="Settings">
<IconSettings />
</ButtonIcon>
}
>
<MenuGroup>
<MenuItem label="Profile" />
<MenuItem label="Statistics" />
</MenuGroup>
<MenuSeparator />
<MenuSub label="Engines and Personas" icon={<IconSettings />}>
<MenuGroup label="Engines">
<MenuItem
label="OpenAI"
icon={<IconOpenAI />}
selected={model === "openai"}
onClick={() => setModel("openai")}
/>
<MenuItem
label="Anthropic"
icon={<IconAnthropic />}
selected={model === "anthropic"}
onClick={() => setModel("anthropic")}
/>
</MenuGroup>
<MenuGroup label="Personas" className="mt-2">
<MenuItem
label="Diggidy"
icon={<IconStarInCircle />}
selected={persona === "diggidy"}
onClick={() => setPersona("diggidy")}
/>
<MenuItem
label="French Teacher"
icon={<IconFrenchFlag />}
selected={persona === "french_teacher"}
onClick={() => setPersona("french_teacher")}
/>
</MenuGroup>
</MenuSub>
<MenuGroup>
<MenuItem label="About" />
</MenuGroup>
</Menu>
);
}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
Menu Props
| Prop | Type | Default | Description |
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- | ---------------- | ---------------------------------------------- |
| trigger | React.ReactElement | - | Element used to open the menu (Button, etc.). |
| children | React.ReactNode | - | MenuItem, MenuSeparator, 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). |
| onOpenChange | (open: boolean) => void | - | Called when menu opens or closes. |
| sideOffset | number | 10 | Offset distance from the trigger element. |
MenuItem 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. |
| onClick | (event) => void | - | Optional click handler. |
| onFocus | (event) => void | - | Optional focus handler. |
| onMouseEnter | (event) => void | - | Optional mouse enter handler. |
MenuSub 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 | 22 | Offset from sub-menu trigger. |
MenuGroup Props
| Prop | Type | Default | Description |
| ----------- | ----------------- | ------- | ----------------------------------------------- |
| label | string | - | Label displayed at the top of the group. |
| icon | React.ReactNode | - | Icon to display on the left of the group label. |
| children | React.ReactNode | - | MenuItems to render inside the group. |
| className | string | - | Custom CSS class for styling. |
MenuSeparator Props
Standard React.HTMLAttributes<HTMLDivElement> - use className for custom styling.
MenuLabel 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. |
