Installation
CLI
npx shadcn@latest add "https://eo-n.vercel.app/r/dropdown-menu"
Manual
Copy and paste the following code into your project.
"use client";
import * as React from "react";
import { Menu as DropdownMenuPrimitive } from "@base-ui-components/react/menu";
import { Check, Circle } from "lucide-react";
import { cn } from "@/lib/utils";
function DropdownMenu({
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />;
}
function DropdownMenuTrigger({
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {
return (
<DropdownMenuPrimitive.Trigger
data-slot="dropdown-menu-trigger"
{...props}
/>
);
}
function DropdownMenuPortal({
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {
return (
<DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />
);
}
function DropdownMenuBackdrop({
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Backdrop>) {
return (
<DropdownMenuPrimitive.Backdrop
data-slot="dropdown-menu-backdrop"
{...props}
/>
);
}
interface DropdownMenuContentProps
extends Omit<
React.ComponentProps<typeof DropdownMenuPrimitive.Positioner>,
"render"
> {}
function DropdownMenuContent({
className,
sideOffset = 4,
children,
...props
}: DropdownMenuContentProps) {
return (
<DropdownMenuPortal>
<DropdownMenuBackdrop />
<DropdownMenuPrimitive.Positioner
data-slot="dropdown-menu-positioner"
sideOffset={sideOffset}
className="z-50 size-auto"
{...props}
>
<DropdownMenuPrimitive.Popup
data-slot="dropdown-menu-content"
className={cn(
"bg-popover text-popover-foreground max-h-[var(--available-height)] max-w-[var(--available-width)] min-w-[8rem] overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md transition-[transform,scale,opacity] duration-150 ease-out",
"origin-[var(--transform-origin)] data-[ending-style]:scale-95 data-[ending-style]:opacity-0 data-[starting-style]:scale-95 data-[starting-style]:opacity-0",
className
)}
>
{children}
</DropdownMenuPrimitive.Popup>
</DropdownMenuPrimitive.Positioner>
</DropdownMenuPortal>
);
}
interface DropdownMenuItemProps
extends React.ComponentProps<typeof DropdownMenuPrimitive.Item> {
inset?: boolean;
variant?: "default" | "destructive";
}
function DropdownMenuItem({
className,
inset,
variant = "default",
...props
}: DropdownMenuItemProps) {
return (
<DropdownMenuPrimitive.Item
data-slot="dropdown-menu-item"
data-inset={inset}
data-variant={variant}
className={cn(
"data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:data-[highlighted]:bg-destructive/10 dark:data-[variant=destructive]:data-[highlighted]:bg-destructive/20 data-[variant=destructive]:data-[highlighted]:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden transition-colors outline-none select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className
)}
{...props}
/>
);
}
function DropdownMenuGroup({
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {
return (
<DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />
);
}
interface DropdownMenuGroupLabelProps
extends React.ComponentProps<typeof DropdownMenuPrimitive.GroupLabel> {
inset?: boolean;
}
function DropdownMenuGroupLabel({
className,
inset,
...props
}: DropdownMenuGroupLabelProps) {
return (
<DropdownMenuPrimitive.GroupLabel
data-slot="dropdown-menu-group-label"
data-inset={inset}
className={cn(
"px-2 py-1.5 text-sm leading-none font-medium data-[inset]:pl-8",
className
)}
{...props}
/>
);
}
function DropdownMenuRadioGroup({
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {
return (
<DropdownMenuPrimitive.RadioGroup
data-slot="dropdown-menu-radio-group"
{...props}
/>
);
}
function DropdownMenuRadioItem({
className,
children,
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) {
return (
<DropdownMenuPrimitive.RadioItem
data-slot="dropdown-menu-radio-item"
className={cn(
"data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm transition-colors outline-none select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className
)}
{...props}
>
<span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
<DropdownMenuPrimitive.RadioItemIndicator className="duration-150 ease-out data-[ending-style]:scale-60 data-[starting-style]:scale-60">
<Circle className="size-2 fill-current" />
</DropdownMenuPrimitive.RadioItemIndicator>
</span>
{children}
</DropdownMenuPrimitive.RadioItem>
);
}
function DropdownMenuCheckboxItem({
className,
children,
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) {
return (
<DropdownMenuPrimitive.CheckboxItem
data-slot="dropdown-menu-checkbox-item"
className={cn(
"data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm transition-colors outline-none select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className
)}
{...props}
>
<span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
<DropdownMenuPrimitive.CheckboxItemIndicator className="duration-150 ease-out data-[ending-style]:scale-60 data-[starting-style]:scale-60">
<Check className="size-4" />
</DropdownMenuPrimitive.CheckboxItemIndicator>
</span>
{children}
</DropdownMenuPrimitive.CheckboxItem>
);
}
function DropdownMenuSubMenuTrigger({
className,
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubmenuTrigger>) {
return (
<DropdownMenuPrimitive.SubmenuTrigger
data-slot="dropdown-menu-sub-menu-trigger"
className={cn(
"data-[popup-open]:bg-accent data-[popup-open]:text-accent-foreground",
className
)}
{...props}
/>
);
}
function DropdownMenuSeparator({
className,
...props
}: Omit<
React.ComponentProps<typeof DropdownMenuPrimitive.Separator>,
"orientation"
>) {
return (
<DropdownMenuPrimitive.Separator
data-slot="dropdown-menu-separator"
className={cn("bg-border -mx-1 my-1 h-px", className)}
{...props}
/>
);
}
function DropdownMenuShortcut({
className,
...props
}: React.ComponentProps<"span">) {
return (
<span
data-slot="dropdown-menu-shortcut"
className={cn(
"text-muted-foreground ml-auto text-xs tracking-widest",
className
)}
{...props}
/>
);
}
export {
DropdownMenu,
DropdownMenuTrigger,
DropdownMenuPortal,
DropdownMenuBackdrop,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuGroup,
DropdownMenuGroupLabel,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuCheckboxItem,
DropdownMenuSubMenuTrigger,
DropdownMenuSeparator,
DropdownMenuShortcut,
};
Update the import paths to match your project setup.
Usage
Import all parts and piece them together.
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
<DropdownMenu>
<DropdownMenuTrigger>Open</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem>Settings</DropdownMenuItem>
<DropdownMenuItem>Account</DropdownMenuItem>
<DropdownMenuItem>Notifications</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>