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.