Menu
A menu of actions opened from a button, with group labels, separators and links.
import { Menu, MenuItem, MenuLinkItem, MenuSeparator, MenuLabel } from '@iffday/ui';Example
Menu opens 6px below its trigger and is at least 210px wide. Focus moves into the menu; the arrow keys move through it, typing jumps to a matching item, and Escape or a click outside closes it and returns focus to the button. Base UI handles all of this.
import { useT } from '@iffday/i18n';
import { Button, Menu, MenuItem, MenuLabel, MenuLinkItem, MenuSeparator } from '@iffday/ui';
import { useApp } from '@iffday/web/src/state';
import { Link } from '@iffday/web/src/router';
import { Ellipsis, Flag, PenLine, Trash2 } from 'lucide-react';
export default function Example() {
const t = useT();
const { notify } = useApp();
return (
<Menu
align="end"
label={t('review.card.reviewActions')}
trigger={
<Button type="button" className="icon-button" aria-label={t('review.card.reviewActions')}>
<Ellipsis size={18} />
</Button>
}
>
<MenuLabel>{t('review.card.reviewActions')}</MenuLabel>
<MenuItem onClick={() => notify(t('common.saved'))}>
<PenLine size={16} />
{t('common.edit')}
</MenuItem>
<MenuItem onClick={() => notify(t('review.card.reviewWithdrawn'))}>
<Trash2 size={16} />
{t('review.card.withdrawMyReview')}
</MenuItem>
<MenuSeparator />
<MenuItem onClick={() => notify(t('review.card.reportSubmitted'))}>
<Flag size={16} />
{t('review.card.report')}
</MenuItem>
<MenuLinkItem render={<Link to="/components/dialog" />}>Dialog</MenuLinkItem>
</Menu>
);
}Usage
- Pass the trigger as
trigger; it keeps its own look. An icon-only trigger needs anaria-label. - For navigation use
MenuLinkItemwith the site’sLinkinrender; the menu closes itself after the click. MenuLabeltitles a group andMenuSeparatordivides groups. Put actions with serious consequences, such as Withdraw and Report, in the last group.
Props
A dropdown menu: Base UI owns focus, typeahead, Escape and outside press.
| Prop | Type | Default |
|---|---|---|
trigger*The trigger element; it is rendered through Base UI so it keeps its own markup. | ReactElement<Record<string, unknown>> | |
children* | ReactNode | |
side | Side | 'bottom' |
align | Align | 'start' |
label | string | |
open | boolean | |
onOpenChange | (open: boolean) => void |
MenuItem
packages/ui/src/overlays.tsx:50| Prop | Type | Default |
|---|---|---|
className | '' |
Other props go to ComponentProps<typeof BaseMenu.Item> (see the Base UI documentation).
MenuLinkItem
packages/ui/src/overlays.tsx:54A navigation entry. Pass the app's router link through `render`.
| Prop | Type | Default |
|---|---|---|
className | '' |
Other props go to ComponentProps<typeof BaseMenu.LinkItem> (see the Base UI documentation).