Menu
A keyboard-navigable dropdown menu with support for items, checkbox items, radio items, and nested submenus.
Usage#
import { Menu } from '@primereact/ui/menu';<Menu.Root>
<Menu.Trigger>Open</Menu.Trigger>
<Menu.Portal>
<Menu.Positioner sideOffset={4}>
<Menu.Popup>
<Menu.List>
<Menu.Group>
<Menu.Label>Account</Menu.Label>
<Menu.Item>Profile</Menu.Item>
<Menu.Item>Settings</Menu.Item>
</Menu.Group>
<Menu.Separator />
<Menu.Group>
<Menu.Item>Sign out</Menu.Item>
</Menu.Group>
</Menu.List>
</Menu.Popup>
</Menu.Positioner>
</Menu.Portal>
</Menu.Root>Examples#
Basic#
A standard dropdown with grouped items, a label, and a separator.
import { Button } from '@primereact/ui/button';
import { Menu } from '@primereact/ui/menu';
export default function Basic() {
return (
<div className="flex justify-center">
<Menu.Root>
<Menu.Trigger as={Button} severity="secondary" variant="outlined">
Account
</Menu.Trigger>
<Menu.Portal>
<Menu.Positioner sideOffset={4}>
<Menu.Popup className="w-40">
<Menu.List>
<Menu.Group>
<Menu.Label>My Account</Menu.Label>
<Menu.Item>Profile</Menu.Item>
<Menu.Item>Billing</Menu.Item>
<Menu.Item>Settings</Menu.Item>
</Menu.Group>
<Menu.Separator />
<Menu.Group>
<Menu.Label>Security</Menu.Label>
<Menu.Item>Change Password</Menu.Item>
<Menu.Item>Two-Factor Auth</Menu.Item>
</Menu.Group>
<Menu.Separator />
<Menu.Group>
<Menu.Item>Invite Members</Menu.Item>
<Menu.Item>Support</Menu.Item>
</Menu.Group>
<Menu.Separator />
<Menu.Group>
<Menu.Item className="text-red-600!">Sign out</Menu.Item>
</Menu.Group>
</Menu.List>
</Menu.Popup>
</Menu.Positioner>
</Menu.Portal>
</Menu.Root>
</div>
);
}
Checkbox & Radio Items#
Use Menu.CheckboxItem for toggleable options and Menu.RadioItemGroup + Menu.RadioItem for mutually exclusive options. Menu.Indicator with match="checked" renders the icon only when the item is selected.
'use client';
import { Check } from '@primeicons/react/check';
import { Dot } from '@primeicons/react/dot';
import { Button } from '@primereact/ui/button';
import { Menu } from '@primereact/ui/menu';
import type { MenuCheckboxItemCheckedChangeEvent, MenuRadioItemGroupValueChangeEvent } from 'primereact/menu';
import * as React from 'react';
export default function CheckboxRadioDemo() {
const [notifications, setNotifications] = React.useState(true);
const [sound, setSound] = React.useState(false);
const [marketing, setMarketing] = React.useState(false);
const [autoUpdate, setAutoUpdate] = React.useState(true);
const [theme, setTheme] = React.useState('light');
const [language, setLanguage] = React.useState('en');
return (
<div className="flex justify-center">
<Menu.Root>
<Menu.Trigger as={Button} severity="secondary" variant="outlined">
Preferences
</Menu.Trigger>
<Menu.Portal>
<Menu.Positioner sideOffset={4}>
<Menu.Popup className="w-40">
<Menu.List>
<Menu.Group>
<Menu.Label>Notifications</Menu.Label>
<Menu.CheckboxItem
checked={notifications}
onCheckedChange={(e: MenuCheckboxItemCheckedChangeEvent) => setNotifications(e.checked)}
>
<Menu.Indicator match="checked">
<Check />
</Menu.Indicator>
Enable notifications
</Menu.CheckboxItem>
<Menu.CheckboxItem
checked={sound}
onCheckedChange={(e: MenuCheckboxItemCheckedChangeEvent) => setSound(e.checked)}
>
<Menu.Indicator match="checked">
<Check />
</Menu.Indicator>
Play sound
</Menu.CheckboxItem>
<Menu.CheckboxItem
checked={marketing}
onCheckedChange={(e: MenuCheckboxItemCheckedChangeEvent) => setMarketing(e.checked)}
>
<Menu.Indicator match="checked">
<Check />
</Menu.Indicator>
Marketing emails
</Menu.CheckboxItem>
</Menu.Group>
<Menu.Separator />
<Menu.Group>
<Menu.Label>System</Menu.Label>
<Menu.CheckboxItem
checked={autoUpdate}
onCheckedChange={(e: MenuCheckboxItemCheckedChangeEvent) => setAutoUpdate(e.checked)}
>
<Menu.Indicator match="checked">
<Check />
</Menu.Indicator>
Auto-update apps
</Menu.CheckboxItem>
</Menu.Group>
<Menu.Separator />
<Menu.Group>
<Menu.Label>Appearance</Menu.Label>
<Menu.RadioItemGroup
value={theme}
onValueChange={(e: MenuRadioItemGroupValueChangeEvent) => setTheme(e.value as string)}
>
<Menu.RadioItem value="light">
<Menu.Indicator match="checked">
<Dot />
</Menu.Indicator>
Light
</Menu.RadioItem>
<Menu.RadioItem value="dark">
<Menu.Indicator match="checked">
<Dot />
</Menu.Indicator>
Dark
</Menu.RadioItem>
<Menu.RadioItem value="system">
<Menu.Indicator match="checked">
<Dot />
</Menu.Indicator>
System
</Menu.RadioItem>
</Menu.RadioItemGroup>
</Menu.Group>
<Menu.Separator />
<Menu.Group>
<Menu.Label>Language</Menu.Label>
<Menu.RadioItemGroup
value={language}
onValueChange={(e: MenuRadioItemGroupValueChangeEvent) => setLanguage(e.value as string)}
>
<Menu.RadioItem value="en">
<Menu.Indicator match="checked">
<Dot />
</Menu.Indicator>
English
</Menu.RadioItem>
<Menu.RadioItem value="tr">
<Menu.Indicator match="checked">
<Dot />
</Menu.Indicator>
Türkçe
</Menu.RadioItem>
<Menu.RadioItem value="de">
<Menu.Indicator match="checked">
<Dot />
</Menu.Indicator>
Deutsch
</Menu.RadioItem>
</Menu.RadioItemGroup>
</Menu.Group>
</Menu.List>
</Menu.Popup>
</Menu.Positioner>
</Menu.Portal>
</Menu.Root>
</div>
);
}
Submenus#
Nest a Menu.Sub inside any Menu.List. The Menu.SubTrigger is the focusable item in the parent menu; the submenu's content lives inside its own Menu.Portal so it positions and animates independently.
'use client';
import { ChevronRight } from '@primeicons/react/chevron-right';
import { Cloud } from '@primeicons/react/cloud';
import { Copy } from '@primeicons/react/copy';
import { Download } from '@primeicons/react/download';
import { Facebook } from '@primeicons/react/facebook';
import { File } from '@primeicons/react/file';
import { FileEdit } from '@primeicons/react/file-edit';
import { FileExport } from '@primeicons/react/file-export';
import { Globe } from '@primeicons/react/globe';
import { Link } from '@primeicons/react/link';
import { Linkedin } from '@primeicons/react/linkedin';
import { Pencil } from '@primeicons/react/pencil';
import { Send } from '@primeicons/react/send';
import { ShareAlt } from '@primeicons/react/share-alt';
import { Twitter } from '@primeicons/react/twitter';
import { Button } from '@primereact/ui/button';
import { Menu } from '@primereact/ui/menu';
export default function SubmenuDemo() {
return (
<div className="flex justify-center">
<Menu.Root>
<Menu.Trigger as={Button} severity="secondary" variant="outlined">
Actions
</Menu.Trigger>
<Menu.Portal>
<Menu.Positioner sideOffset={4}>
<Menu.Popup className="w-44">
<Menu.List>
<Menu.Group>
<Menu.Label>Document</Menu.Label>
<Menu.Item>
<File />
New file
</Menu.Item>
<Menu.Item>
<FileEdit />
Open recent
</Menu.Item>
<Menu.Item>
<Copy />
Duplicate
</Menu.Item>
<Menu.Sub>
<Menu.SubTrigger>
<Download />
Import
<Menu.Indicator>
<ChevronRight className="size-3.5" />
</Menu.Indicator>
</Menu.SubTrigger>
<Menu.Portal>
<Menu.Positioner>
<Menu.Popup className="w-44">
<Menu.List>
<Menu.Group>
<Menu.Item>
<File />
From file
</Menu.Item>
<Menu.Item>
<Cloud />
From cloud
</Menu.Item>
<Menu.Item>
<Globe />
From URL
</Menu.Item>
</Menu.Group>
</Menu.List>
</Menu.Popup>
</Menu.Positioner>
</Menu.Portal>
</Menu.Sub>
<Menu.Sub>
<Menu.SubTrigger>
<FileExport />
Export
<Menu.Indicator>
<ChevronRight className="size-3.5" />
</Menu.Indicator>
</Menu.SubTrigger>
<Menu.Portal>
<Menu.Positioner>
<Menu.Popup className="w-44">
<Menu.List>
<Menu.Group>
<Menu.Item>PDF</Menu.Item>
<Menu.Item>Word</Menu.Item>
<Menu.Item>Markdown</Menu.Item>
<Menu.Item>HTML</Menu.Item>
<Menu.Sub>
<Menu.SubTrigger>
More
<Menu.Indicator>
<ChevronRight className="size-3.5" />
</Menu.Indicator>
</Menu.SubTrigger>
<Menu.Portal>
<Menu.Positioner>
<Menu.Popup className="w-40">
<Menu.List>
<Menu.Group>
<Menu.Item>EPUB</Menu.Item>
<Menu.Item>RTF</Menu.Item>
<Menu.Item>LaTeX</Menu.Item>
<Menu.Item>Plain Text</Menu.Item>
</Menu.Group>
</Menu.List>
</Menu.Popup>
</Menu.Positioner>
</Menu.Portal>
</Menu.Sub>
</Menu.Group>
</Menu.List>
</Menu.Popup>
</Menu.Positioner>
</Menu.Portal>
</Menu.Sub>
<Menu.Sub>
<Menu.SubTrigger>
<ShareAlt />
Share
<Menu.Indicator>
<ChevronRight className="size-3.5" />
</Menu.Indicator>
</Menu.SubTrigger>
<Menu.Portal>
<Menu.Positioner>
<Menu.Popup className="w-40">
<Menu.List>
<Menu.Group>
<Menu.Item>
<Send />
Send via email
</Menu.Item>
<Menu.Item>
<Link />
Copy link
</Menu.Item>
</Menu.Group>
<Menu.Separator />
<Menu.Group>
<Menu.Label>Social</Menu.Label>
<Menu.Item>
<Twitter />
Twitter
</Menu.Item>
<Menu.Item>
<Facebook />
Facebook
</Menu.Item>
<Menu.Item>
<Linkedin />
LinkedIn
</Menu.Item>
</Menu.Group>
</Menu.List>
</Menu.Popup>
</Menu.Positioner>
</Menu.Portal>
</Menu.Sub>
<Menu.Item>
<Pencil />
Rename
</Menu.Item>
</Menu.Group>
</Menu.List>
</Menu.Popup>
</Menu.Positioner>
</Menu.Portal>
</Menu.Root>
</div>
);
}
Inline#
Menu works without a trigger or portal — drop a Menu.List directly inside it for sidebar-style navigation menus.
'use client';
import { Bell } from '@primeicons/react/bell';
import { Bookmark } from '@primeicons/react/bookmark';
import { Briefcase } from '@primeicons/react/briefcase';
import { ChartBar } from '@primeicons/react/chart-bar';
import { ChevronDown } from '@primeicons/react/chevron-down';
import { Clock } from '@primeicons/react/clock';
import { Cog } from '@primeicons/react/cog';
import { CreditCard } from '@primeicons/react/credit-card';
import { Folder } from '@primeicons/react/folder';
import { Home } from '@primeicons/react/home';
import { Inbox } from '@primeicons/react/inbox';
import { PowerOff } from '@primeicons/react/power-off';
import { QuestionCircle } from '@primeicons/react/question-circle';
import { Star } from '@primeicons/react/star';
import { User } from '@primeicons/react/user';
import { Users } from '@primeicons/react/users';
import { Menu } from '@primereact/ui/menu';
export default function InlineDemo() {
return (
<Menu.Root closeOnSelect={false} composite={false} className="w-64 mx-auto">
<Menu.List>
<Menu.Group>
<Menu.Label>Workspace</Menu.Label>
<Menu.Item>
<Home />
Dashboard
</Menu.Item>
<Menu.Item>
<Inbox />
Inbox
</Menu.Item>
<Menu.Sub defaultOpen openOnHover={false}>
<Menu.SubTrigger>
<Folder />
Projects
<Menu.Indicator>
<ChevronDown className="size-3.5 transition-transform duration-200 [[data-open]>*>&]:rotate-180" />
</Menu.Indicator>
</Menu.SubTrigger>
<Menu.List className="ms-6 hidden in-data-[part=sub]:in-data-open:flex">
<Menu.Group>
<Menu.Item>
<Briefcase />
Active
</Menu.Item>
<Menu.Item>
<Clock />
Recent
</Menu.Item>
<Menu.Item>
<Star />
Favorites
</Menu.Item>
</Menu.Group>
</Menu.List>
</Menu.Sub>
<Menu.Item>
<ChartBar />
Analytics
</Menu.Item>
<Menu.Item>
<Users />
Team
</Menu.Item>
</Menu.Group>
<Menu.Separator />
<Menu.Group>
<Menu.Label>Account</Menu.Label>
<Menu.Item>
<User />
Profile
</Menu.Item>
<Menu.Item>
<Bell />
Notifications
</Menu.Item>
<Menu.Item>
<Bookmark />
Saved
</Menu.Item>
<Menu.Item>
<CreditCard />
Billing
</Menu.Item>
<Menu.Item>
<Cog />
Settings
</Menu.Item>
</Menu.Group>
<Menu.Separator />
<Menu.Group>
<Menu.Item>
<QuestionCircle />
Help & Support
</Menu.Item>
<Menu.Item className="text-red-600!">
<PowerOff />
Sign out
</Menu.Item>
</Menu.Group>
</Menu.List>
</Menu.Root>
);
}
Hover#
Pass openOnHover to make the trigger open the menu on pointer hover (in addition to click). Use openDelay and closeDelay (in ms) to fine-tune the timing. Click always activates immediately and bypasses the delays.
'use client';
import { Bell } from '@primeicons/react/bell';
import { Cog } from '@primeicons/react/cog';
import { CreditCard } from '@primeicons/react/credit-card';
import { PowerOff } from '@primeicons/react/power-off';
import { User } from '@primeicons/react/user';
import { Button } from '@primereact/ui/button';
import { Menu } from '@primereact/ui/menu';
export default function HoverDemo() {
return (
<div className="flex justify-center">
<Menu.Root openOnHover openDelay={100} closeDelay={150}>
<Menu.Trigger as={Button} severity="secondary" variant="outlined">
Account
</Menu.Trigger>
<Menu.Portal>
<Menu.Positioner sideOffset={4}>
<Menu.Popup className="w-44">
<Menu.List>
<Menu.Group>
<Menu.Label>My Account</Menu.Label>
<Menu.Item>
<User />
Profile
</Menu.Item>
<Menu.Item>
<Bell />
Notifications
</Menu.Item>
<Menu.Item>
<CreditCard />
Billing
</Menu.Item>
<Menu.Item>
<Cog />
Settings
</Menu.Item>
</Menu.Group>
<Menu.Separator />
<Menu.Group>
<Menu.Item className="text-red-600!">
<PowerOff />
Sign out
</Menu.Item>
</Menu.Group>
</Menu.List>
</Menu.Popup>
</Menu.Positioner>
</Menu.Portal>
</Menu.Root>
</div>
);
}
Apps#
The Menu component can be customized to create app launchers or grid-based menus. By applying custom classes to Menu.List and Menu.Item, the menu layout can be transformed into a grid structure with custom styling for icons and labels.
import { Bars } from '@primeicons/react/bars';
import { Calendar } from '@primeicons/react/calendar';
import { ChartLine } from '@primeicons/react/chart-line';
import { Cloud } from '@primeicons/react/cloud';
import { Cog } from '@primeicons/react/cog';
import { Envelope } from '@primeicons/react/envelope';
import { Image } from '@primeicons/react/image';
import { MapMarker } from '@primeicons/react/map-marker';
import { Search } from '@primeicons/react/search';
import { Video } from '@primeicons/react/video';
import { Menu } from '@primereact/ui/menu';
import * as React from 'react';
const apps = [
{ label: 'Search', icon: <Search />, gradient: 'from-sky-400 to-cyan-500' },
{ label: 'Maps', icon: <MapMarker />, gradient: 'from-emerald-500 to-green-600' },
{ label: 'Mail', icon: <Envelope />, gradient: 'from-orange-400 to-red-500' },
{ label: 'Drive', icon: <Cloud />, gradient: 'from-blue-500 to-indigo-600' },
{ label: 'Calendar', icon: <Calendar />, gradient: 'from-violet-500 to-purple-600' },
{ label: 'Photos', icon: <Image />, gradient: 'from-fuchsia-500 to-pink-600' },
{ label: 'Videos', icon: <Video />, gradient: 'from-red-500 to-rose-600' },
{ label: 'Analytics', icon: <ChartLine />, gradient: 'from-cyan-500 to-blue-600' },
{ label: 'Settings', icon: <Cog />, gradient: 'from-slate-500 to-zinc-700' }
];
export default function AppsDemo() {
return (
<div className="flex justify-center">
<Menu.Root className="w-116">
<Menu.Trigger className="px-2.5 h-8 rounded-lg border border-surface flex items-center justify-center gap-2 bg-surface-0 dark:bg-surface-950 hover:bg-surface-100 dark:hover:bg-surface-900 data-open:bg-surface-100 dark:data-open:bg-surface-900 transition-colors text-sm font-medium focus-visible:outline focus-visible:outline-1 focus-visible:outline-offset-2 focus-visible:outline-primary">
<Bars className="w-6! h-6!" />
</Menu.Trigger>
<Menu.Portal>
<Menu.Positioner align="end" sideOffset={12}>
<Menu.Popup>
<Menu.List className="grid grid-cols-3 gap-1 p-2">
{apps.map((app) => (
<Menu.Item key={app.label} className="flex-col items-center justify-center h-26 gap-3">
<div
className={`w-14 h-14 rounded-2xl bg-linear-to-br ${app.gradient} flex items-center justify-center transition-transform`}
>
{React.cloneElement(app.icon, { className: 'w-6! h-6! text-white' })}
</div>
<span className="text-sm font-medium">{app.label}</span>
</Menu.Item>
))}
</Menu.List>
</Menu.Popup>
</Menu.Positioner>
</Menu.Portal>
</Menu.Root>
</div>
);
}
API#
Sub-Components#
See Primitive API for MenuRoot, MenuTrigger, MenuPortal, MenuPositioner, MenuPopup, MenuArrow, MenuList, MenuGroup, MenuLabel, MenuSeparator, MenuItem, MenuCheckboxItem, MenuRadioItemGroup, MenuRadioItem, MenuSub, MenuSubTrigger, MenuIndicator component documentation.
Hooks#
See Headless API for useMenu and useMenuSub hook documentation.
Accessibility#
See Menu Primitive for WAI-ARIA compliance details and keyboard support.