Menu

PreviousNext

A menu displays a list of actions or options that a user can choose.

"use client"

import { SubmenuTrigger } from "react-aria-components"

import { Button } from "@/components/ui/button"
import {
  Menu,
  MenuItem,
  MenuSection,
  MenuSeparator,
  MenuTrigger,
} from "@/components/ui/menu"

export function MenuDemo() {
  return (
    <MenuTrigger>
      <Button variant="outline">Open</Button>
      <Menu placement="bottom start" className="w-56">
        <MenuSection title="My Account">
          <MenuItem onAction={() => console.log("Profile")}>
            Profile
            <span className="text-muted-foreground ml-auto text-xs tracking-widest">
              ⇧⌘P
            </span>
          </MenuItem>
          <MenuItem onAction={() => console.log("Billing")}>
            Billing
            <span className="text-muted-foreground ml-auto text-xs tracking-widest">
              ⌘B
            </span>
          </MenuItem>
          <MenuItem onAction={() => console.log("Settings")}>
            Settings
            <span className="text-muted-foreground ml-auto text-xs tracking-widest">
              ⌘S
            </span>
          </MenuItem>
          <MenuItem onAction={() => console.log("Keyboard shortcuts")}>
            Keyboard shortcuts
            <span className="text-muted-foreground ml-auto text-xs tracking-widest">
              ⌘K
            </span>
          </MenuItem>
        </MenuSection>
        <MenuSeparator />
        <MenuSection>
          <MenuItem onAction={() => console.log("Team")}>Team</MenuItem>
          <SubmenuTrigger>
            <MenuItem>Invite users</MenuItem>
            <Menu className="w-56">
              <MenuItem onAction={() => console.log("Email")}>Email</MenuItem>
              <MenuItem onAction={() => console.log("Message")}>
                Message
              </MenuItem>
              <MenuSeparator />
              <MenuItem onAction={() => console.log("More")}>More...</MenuItem>
            </Menu>
          </SubmenuTrigger>
          <MenuItem onAction={() => console.log("New Team")}>
            New Team
            <span className="text-muted-foreground ml-auto text-xs tracking-widest">
              ⌘+T
            </span>
          </MenuItem>
        </MenuSection>
        <MenuSeparator />
        <MenuItem onAction={() => console.log("GitHub")}>GitHub</MenuItem>
        <MenuItem onAction={() => console.log("Support")}>Support</MenuItem>
        <MenuItem isDisabled>API</MenuItem>
        <MenuSeparator />
        <MenuItem onAction={() => console.log("Log out")}>
          Log out
          <span className="text-muted-foreground ml-auto text-xs tracking-widest">
            ⇧⌘Q
          </span>
        </MenuItem>
      </Menu>
    </MenuTrigger>
  )
}

Installation

pnpm dlx shadcn@latest add menu

Usage

import {
  Menu,
  MenuItem,
  MenuSection,
  MenuSeparator,
  MenuTrigger,
} from "@/components/ui/menu"
<MenuTrigger>
  <Button variant="outline">Open</Button>
  <Menu>
    <MenuItem onAction={() => console.log("New")}>New</MenuItem>
    <MenuItem onAction={() => console.log("Open")}>Open</MenuItem>
    <MenuSeparator />
    <MenuItem onAction={() => console.log("Save")}>Save</MenuItem>
  </Menu>
</MenuTrigger>

Examples

"use client"

import { SubmenuTrigger } from "react-aria-components"

import { Button } from "@/components/ui/button"
import {
  Menu,
  MenuItem,
  MenuSeparator,
  MenuTrigger,
} from "@/components/ui/menu"

export function MenuSubmenuDemo() {
  return (
    <MenuTrigger>
      <Button variant="outline">Edit</Button>
      <Menu className="w-56">
        <MenuItem onAction={() => console.log("Undo")}>
          Undo
          <span className="text-muted-foreground ml-auto text-xs tracking-widest">
            ⌘Z
          </span>
        </MenuItem>
        <MenuItem onAction={() => console.log("Redo")}>
          Redo
          <span className="text-muted-foreground ml-auto text-xs tracking-widest">
            ⇧⌘Z
          </span>
        </MenuItem>
        <MenuSeparator />
        <MenuItem onAction={() => console.log("Cut")}>
          Cut
          <span className="text-muted-foreground ml-auto text-xs tracking-widest">
            ⌘X
          </span>
        </MenuItem>
        <MenuItem onAction={() => console.log("Copy")}>
          Copy
          <span className="text-muted-foreground ml-auto text-xs tracking-widest">
            ⌘C
          </span>
        </MenuItem>
        <MenuItem onAction={() => console.log("Paste")}>
          Paste
          <span className="text-muted-foreground ml-auto text-xs tracking-widest">
            ⌘V
          </span>
        </MenuItem>
        <MenuSeparator />
        <SubmenuTrigger>
          <MenuItem>Share</MenuItem>
          <Menu className="w-56">
            <MenuItem onAction={() => console.log("Email")}>Email</MenuItem>
            <MenuItem onAction={() => console.log("Copy Link")}>
              Copy Link
            </MenuItem>
            <MenuSeparator />
            <SubmenuTrigger>
              <MenuItem>Social Media</MenuItem>
              <Menu className="w-56">
                <MenuItem onAction={() => console.log("Twitter")}>
                  Twitter
                </MenuItem>
                <MenuItem onAction={() => console.log("Facebook")}>
                  Facebook
                </MenuItem>
                <MenuItem onAction={() => console.log("LinkedIn")}>
                  LinkedIn
                </MenuItem>
              </Menu>
            </SubmenuTrigger>
          </Menu>
        </SubmenuTrigger>
        <SubmenuTrigger>
          <MenuItem>Export</MenuItem>
          <Menu className="w-56">
            <MenuItem onAction={() => console.log("PDF")}>PDF</MenuItem>
            <MenuItem onAction={() => console.log("PNG")}>PNG</MenuItem>
            <MenuItem onAction={() => console.log("SVG")}>SVG</MenuItem>
            <MenuSeparator />
            <MenuItem onAction={() => console.log("HTML")}>HTML</MenuItem>
            <MenuItem onAction={() => console.log("Markdown")}>
              Markdown
            </MenuItem>
          </Menu>
        </SubmenuTrigger>
      </Menu>
    </MenuTrigger>
  )
}

Menus support nested submenus. Use the SubmenuTrigger component from react-aria-components to create a submenu.

import { SubmenuTrigger } from "react-aria-components"
import { Menu, MenuItem, MenuTrigger } from "@/components/ui/menu"
 
<MenuTrigger>
  <Button variant="outline">Edit</Button>
  <Menu>
    <MenuItem>Cut</MenuItem>
    <MenuItem>Copy</MenuItem>
    <MenuItem>Paste</MenuItem>
    <MenuSeparator />
    <SubmenuTrigger>
      <MenuItem>Share</MenuItem>
      <Menu>
        <MenuItem>Email</MenuItem>
        <MenuItem>Copy Link</MenuItem>
      </Menu>
    </SubmenuTrigger>
  </Menu>
</MenuTrigger>

With Command

"use client"

import { SubmenuTrigger } from "react-aria-components"

import {
  Avatar,
  AvatarFallback,
  AvatarImage,
} from "@/components/ui/avatar"
import { Button } from "@/components/ui/button"
import {
  Command,
  CommandDialog,
  CommandEmpty,
  CommandGroup,
  CommandInput,
  CommandItem,
  CommandList,
} from "@/components/ui/command"
import {
  Menu,
  MenuItem,
  MenuSeparator,
  MenuTrigger,
} from "@/components/ui/menu"

import { Popover } from "../ui/popover"

const TEAM_MEMBERS = [
  {
    name: "shadcn",
    email: "shadcn@example.com",
    avatar: "https://github.com/shadcn.png",
  },
  {
    name: "maxleiter",
    email: "max@example.com",
    avatar: "https://github.com/maxleiter.png",
  },
  {
    name: "evilrabbit",
    email: "rabbit@example.com",
    avatar: "https://github.com/evilrabbit.png",
  },
]

const PROJECTS = [
  { name: "Design System", emoji: "🎨" },
  { name: "Marketing Site", emoji: "📱" },
  { name: "Dashboard", emoji: "📊" },
  { name: "Documentation", emoji: "📚" },
  { name: "API Gateway", emoji: "🔌" },
]

export function MenuCommandDemo() {
  return (
    <MenuTrigger>
      <Button variant="outline">Assign Task</Button>
      <Menu className="w-56">
        <MenuItem onAction={() => console.log("Assign to me")}>
          Assign to me
        </MenuItem>
        <MenuSeparator />
        <SubmenuTrigger>
          <MenuItem>Assign to team member</MenuItem>
          <Popover>
            <Command>
              <CommandInput placeholder="Search team members..." autoFocus />
              <CommandList
                renderEmptyState={() => (
                  <CommandEmpty>No team member found</CommandEmpty>
                )}
              >
                <CommandGroup>
                  {TEAM_MEMBERS.map((member) => (
                    <CommandItem
                      key={member.email}
                      id={member.email}
                      onSelect={() => {
                        console.log("Assigned to:", member.name)
                      }}
                    >
                      <span className="mr-2 inline-flex h-6 w-6 overflow-hidden rounded-full align-middle">
                        <img src={member.avatar} alt={member.name} />
                      </span>
                      <span className="ml-2">{member.name}</span>
                      <span className="text-muted-foreground ml-auto text-xs">
                        {member.email}
                      </span>
                    </CommandItem>
                  ))}
                </CommandGroup>
              </CommandList>
            </Command>
          </Popover>
        </SubmenuTrigger>
        <SubmenuTrigger>
          <MenuItem>Move to project</MenuItem>
          <Popover>
            <Command>
              <CommandInput placeholder="Search projects..." autoFocus />
              <CommandList
                renderEmptyState={() => (
                  <CommandEmpty>No project found</CommandEmpty>
                )}
              >
                <CommandGroup>
                  {PROJECTS.map((project) => (
                    <CommandItem
                      key={project.name}
                      id={project.name}
                      onSelect={() => {
                        console.log("Moved to:", project.name)
                      }}
                    >
                      <span className="mr-2">{project.emoji}</span>
                      {project.name}
                    </CommandItem>
                  ))}
                </CommandGroup>
              </CommandList>
            </Command>
          </Popover>
        </SubmenuTrigger>
        <MenuSeparator />
        <MenuItem onAction={() => console.log("Unassign")}>Unassign</MenuItem>
      </Menu>
    </MenuTrigger>
  )
}

You can combine Menu with Command to create searchable submenus. This is useful when you have a large list of options that need to be filtered.

import { SubmenuTrigger } from "react-aria-components"
import {
  Command,
  CommandEmpty,
  CommandGroup,
  CommandInput,
  CommandItem,
  CommandList,
} from "@/components/ui/command"
import { Menu, MenuItem, MenuTrigger } from "@/components/ui/menu"
 
<MenuTrigger>
  <Button variant="outline">Assign Task</Button>
  <Menu>
    <MenuItem>Assign to me</MenuItem>
    <SubmenuTrigger>
      <MenuItem>Assign to team member</MenuItem>
      <Menu className="w-72 p-0">
        <Command>
          <CommandInput placeholder="Search team members..." autoFocus />
          <CommandList>
            <CommandEmpty>No team member found</CommandEmpty>
            <CommandGroup>
              {/* Your command items here */}
            </CommandGroup>
          </CommandList>
        </Command>
      </Menu>
    </SubmenuTrigger>
  </Menu>
</MenuTrigger>