GitHub

Dropdown Menu

Compact action menu for contextual operations.

01 Live Demo

Last action: None

02 Code Snippet

src/ui/DropdownMenu.tsx
import { DropdownMenu } from "@/ui/DropdownMenu";

<DropdownMenu.Root>
  <DropdownMenu.Trigger>Actions</DropdownMenu.Trigger>
  <DropdownMenu.Content>
    <DropdownMenu.Label>Profile</DropdownMenu.Label>
    <DropdownMenu.Item onSelect={() => console.log("Edit")}>Edit profile</DropdownMenu.Item>
    <DropdownMenu.Item onSelect={() => console.log("Invite")}>Invite member</DropdownMenu.Item>
    <DropdownMenu.Separator />
    <DropdownMenu.Item onSelect={() => console.log("Delete")}>Delete project</DropdownMenu.Item>
  </DropdownMenu.Content>
</DropdownMenu.Root>

03 Copy-Paste (Single File)

DropdownMenu.tsx
import { useState, type ReactNode } from "react";

function DropdownMenuRoot({ children }: { children: ReactNode }) {
  return <div className="relative inline-block">{children}</div>;
}

function DropdownMenuContent({ open, children }: { open: boolean; children: ReactNode }) {
  if (!open) return null;
  return <div className="absolute right-0 top-[calc(100%+8px)] min-w-56 rounded-xl border border-slate-200 bg-white p-1 shadow-xl">{children}</div>;
}

function DropdownMenuItem({ onClick, children }: { onClick?: () => void; children: ReactNode }) {
  return <button type="button" onClick={onClick} className="flex w-full rounded-lg px-2.5 py-2 text-left text-sm hover:bg-slate-50">{children}</button>;
}

type DropdownMenuCompound = typeof DropdownMenuRoot & {
  Root: typeof DropdownMenuRoot;
  Content: typeof DropdownMenuContent;
  Item: typeof DropdownMenuItem;
};

export const DropdownMenu = Object.assign(DropdownMenuRoot, { Root: DropdownMenuRoot, Content: DropdownMenuContent, Item: DropdownMenuItem }) as DropdownMenuCompound;

function Example() {
  const [open, setOpen] = useState(false);
  return (
    <DropdownMenu.Root>
      <button onClick={() => setOpen((v) => !v)}>Open</button>
      <DropdownMenu.Content open={open}>
        <DropdownMenu.Item onClick={() => setOpen(false)}>Edit</DropdownMenu.Item>
      </DropdownMenu.Content>
    </DropdownMenu.Root>
  );
}
react-principles