Docs
Menu

Menu

Dropdown menu opened via button. Supports typeahead and keyboard navigation.

Defualt

Menu extends the Button component.

Installation

Install the following dependencies:

npm install @radix-ui/react-dropdown-menu

Copy and paste the following code into your project.

"use client"
 
import * as React from "react"
import Link from "next/link"
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
import { Check, Circle } from "lucide-react"
 
import { cn } from "@/lib/utils"
 
import { Button } from "./button"
 
const MenuContainer = ({ children }: { children: React.ReactNode }) => (
  <DropdownMenuPrimitive.Root modal={false}>
    {children}
  </DropdownMenuPrimitive.Root>
)
 
const MenuButton = React.forwardRef<
  React.ElementRef<typeof DropdownMenuPrimitive.Trigger>,
  React.ComponentPropsWithoutRef<typeof Button>
>(({ ...props }, ref) => (
  <DropdownMenuPrimitive.Trigger asChild ref={ref}>
    <Button {...props} />
  </DropdownMenuPrimitive.Trigger>
))
MenuButton.displayName = DropdownMenuPrimitive.Item.displayName
 
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup
 
const Menu = React.forwardRef<
  React.ElementRef<typeof DropdownMenuPrimitive.Content>,
  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content> & {
    width?: number
  }
>(({ className, sideOffset = 8, width, align = "start", ...props }, ref) => (
  <DropdownMenuPrimitive.Portal>
    <DropdownMenuPrimitive.Content
      ref={ref}
      sideOffset={sideOffset}
      className={cn(
        "font-sm z-50 min-w-[150px] overflow-hidden rounded-xl bg-background-100 p-2 text-popover-foreground shadow-menu data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
        className
      )}
      align={align}
      style={{ width: width ? `${width}px` : undefined }}
      {...props}
    />
  </DropdownMenuPrimitive.Portal>
))
Menu.displayName = DropdownMenuPrimitive.Content.displayName
 
const MenuItem = React.forwardRef<
  React.ElementRef<typeof DropdownMenuPrimitive.Item>,
  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
    type?: "normal" | "error"
  }
>(({ className, type, ...props }, ref) => (
  <DropdownMenuPrimitive.Item
    ref={ref}
    className={cn(
      "relative flex h-10 cursor-pointer select-none items-center rounded-md px-2 text-sm text-gray-1000 outline-none transition-colors focus:bg-gray-alpha-100 data-[disabled]:pointer-events-none data-[disabled]:text-gray-700",
      type === "error" && "text-red-900",
      className
    )}
    {...props}
  />
))
MenuItem.displayName = DropdownMenuPrimitive.Item.displayName
 
const MenuLink = React.forwardRef<
  React.ElementRef<typeof DropdownMenuPrimitive.Item>,
  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
    href: string
  }
>(({ className, href, children, ...props }, ref) => (
  <MenuItem asChild {...props}>
    <Link href={href}>{children}</Link>
  </MenuItem>
))
MenuLink.displayName = DropdownMenuPrimitive.Item.displayName
 
const DropdownMenuCheckboxItem = React.forwardRef<
  React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>
>(({ className, children, checked, ...props }, ref) => (
  <DropdownMenuPrimitive.CheckboxItem
    ref={ref}
    className={cn(
      "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
      className
    )}
    checked={checked}
    {...props}
  >
    <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
      <DropdownMenuPrimitive.ItemIndicator>
        <Check className="h-4 w-4" />
      </DropdownMenuPrimitive.ItemIndicator>
    </span>
    {children}
  </DropdownMenuPrimitive.CheckboxItem>
))
DropdownMenuCheckboxItem.displayName =
  DropdownMenuPrimitive.CheckboxItem.displayName
 
const DropdownMenuRadioItem = React.forwardRef<
  React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>
>(({ className, children, ...props }, ref) => (
  <DropdownMenuPrimitive.RadioItem
    ref={ref}
    className={cn(
      "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
      className
    )}
    {...props}
  >
    <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
      <DropdownMenuPrimitive.ItemIndicator>
        <Circle className="h-2 w-2 fill-current" />
      </DropdownMenuPrimitive.ItemIndicator>
    </span>
    {children}
  </DropdownMenuPrimitive.RadioItem>
))
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName
 
const DropdownMenuLabel = React.forwardRef<
  React.ElementRef<typeof DropdownMenuPrimitive.Label>,
  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
    inset?: boolean
  }
>(({ className, inset, ...props }, ref) => (
  <DropdownMenuPrimitive.Label
    ref={ref}
    className={cn(
      "px-2 py-1.5 text-sm font-semibold",
      inset && "pl-8",
      className
    )}
    {...props}
  />
))
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName
 
const DropdownMenuShortcut = ({
  className,
  ...props
}: React.HTMLAttributes<HTMLSpanElement>) => {
  return (
    <span
      className={cn("ml-auto text-xs tracking-widest opacity-60", className)}
      {...props}
    />
  )
}
DropdownMenuShortcut.displayName = "DropdownMenuShortcut"
 
export {
  MenuContainer,
  Menu,
  MenuItem,
  MenuLink,
  MenuButton,
  DropdownMenuCheckboxItem,
  DropdownMenuRadioItem,
  DropdownMenuLabel,
  DropdownMenuShortcut,
  DropdownMenuRadioGroup,
}

Update the import paths to match your project setup.

Usage

import {
  DropdownMenuLabel,
  Menu,
  MenuButton,
  MenuContainer,
  MenuItem,
} from "@/components/ui/dropdown-menu"
<MenuContainer>
  <MenuButton>Open</MenuButton>
  <Menu>
    <DropdownMenuLabel>My Account</DropdownMenuLabel>
    <MenuItem>Profile</MenuItem>
    <MenuItem>Billing</MenuItem>
    <MenuItem>Team</MenuItem>
    <MenuItem>Subscription</MenuItem>
  </Menu>
</MenuContainer>

Disabled items

Link items

Custom trigger

Prefix and suffix

The trigger is still wrapped by an unstyled button.

Menu position

The position will automatically adapt based on the window bounds.

Radio Group