Overview
The ContextMenu component provides a right-click menu with customizable options and actions.Basic Usage
- Code
- Preview
import { useState } from "react";
import ContextMenu from "@/ui/context-menu";
import { Copy } from "@/assets/icons/copy.tsx";
import { HouseAdd } from "@/assets/icons/house-add.tsx";
import { TrashEmpty } from "@/assets/icons/trash-empty.tsx";
export default function ContextMenuBasicExample() {
const [lastAction, setLastAction] = useState<string>("None");
return (
<div className="flex items-center flex-col gap-4 w-full">
<ContextMenu.Root>
<ContextMenu.Trigger trigger="right">
<div className="font-mono w-full max-w-md rounded-lg border-2 border-dashed border-neutral-80 bg-neutral-98 p-6 text-center text-sm text-neutral-40">
Right-click anywhere in this area
</div>
</ContextMenu.Trigger>
<ContextMenu.Content>
<ContextMenu.List>
<ContextMenu.Item icon={<Copy />} onSelect={() => setLastAction("copy")}>
Copy
</ContextMenu.Item>
<ContextMenu.Item icon={<HouseAdd />} onSelect={() => setLastAction("paste")}>
Paste
</ContextMenu.Item>
<ContextMenu.Item
icon={<TrashEmpty />}
onSelect={() => setLastAction("delete")}
>
Delete
</ContextMenu.Item>
</ContextMenu.List>
</ContextMenu.Content>
</ContextMenu.Root>
<div className="text-sm text-neutral-60 font-mono">
Last action: <span className="font-medium text-neutral-0">{lastAction}</span>
</div>
</div>
);
}
| Component | Prop | Type | Default | Description |
|---|---|---|---|---|
| Root | children | React.ReactNode | — | Wraps trigger and menu content |
| Root | className | string | — | Additional classes for wrapper |
| Trigger | children | React.ReactNode | — | Element that opens the menu |
| Trigger | trigger | 'left' | 'right' | 'both' | 'left' | Choose which mouse button opens the menu |
| Trigger | className | string | — | Additional classes |
| Content | children | React.ReactNode | — | Menu body |
| Content | width | number | 260 | Menu width in pixels |
| Content | placement | 'auto' | 'bottom-start' | 'bottom-end' | 'top-start' | 'top-end' | 'auto' | Preferred menu placement |
| Content | offset | number | 6 | Distance between trigger and menu |
| Content | className | string | — | Additional classes |
| List | children | React.ReactNode | — | Menu list container |
| List | className | string | — | Additional classes |
| Label | children | React.ReactNode | — | Section heading text |
| Item | children | React.ReactNode | — | Item label |
| Item | icon | React.ReactNode | — | Optional leading icon |
| Item | shortcut | string | — | Keyboard shortcut helper text |
| Item | onSelect | () => void | — | Selection handler |
| Item | disabled | boolean | false | Disable item interaction |
| Item | className | string | — | Additional classes |
| Group | value | string | — | Selected value (radio behaviour) |
| Group | onValueChange | (value: string) => void | — | Selection change handler |
Examples
File Context Menu
- Code
- Preview
import { useState } from "react";
import ContextMenu from "@/ui/context-menu";
import { Eye } from "@/assets/icons/eye.tsx";
import { EditPencil } from "@/assets/icons/edit-pencil.tsx";
import { Email } from "@/assets/icons/email.tsx";
import { Copy } from "@/assets/icons/copy.tsx";
import { TrashEmpty } from "@/assets/icons/trash-empty.tsx";
const FILE = {
id: 1,
name: "Quarterly_Report.pdf",
size: "2.4 MB",
};
export default function ContextMenuFileExample() {
const [lastAction, setLastAction] = useState("No action yet");
const handleAction = (action: string) => {
setLastAction(`${action} → ${FILE.name}`);
};
return (
<div className="flex items-center flex-col gap-4 w-full">
<ContextMenu.Root>
<ContextMenu.Trigger trigger="right">
<div className="w-full max-w-md cursor-pointer rounded-lg border border-neutral-200 bg-white p-4 text-sm shadow-sm hover:bg-neutral-98">
<div className="font-medium text-neutral-0">{FILE.name}</div>
<div className="text-neutral-40">{FILE.size}</div>
<div className="mt-2 text-xs text-neutral-50 font-mono">Right-click to manage file</div>
</div>
</ContextMenu.Trigger>
<ContextMenu.Content>
<ContextMenu.List>
<ContextMenu.Item icon={<Eye />} onSelect={() => handleAction("View file")}>
View
</ContextMenu.Item>
<ContextMenu.Item icon={<EditPencil />} onSelect={() => handleAction("Edit file")}>
Edit
</ContextMenu.Item>
<ContextMenu.Item icon={<Email />} onSelect={() => handleAction("Share via email")}>Email</ContextMenu.Item>
<ContextMenu.Item icon={<Copy />} onSelect={() => handleAction("Share link")}>Copy Link</ContextMenu.Item>
<ContextMenu.Item icon={<TrashEmpty />} className="[&>svg]:text-error-50 text-error-50" onSelect={() => handleAction("Delete file")}>
Delete
</ContextMenu.Item>
</ContextMenu.List>
</ContextMenu.Content>
</ContextMenu.Root>
<div className="text-sm text-neutral-60 font-mono">
{lastAction}
</div>
</div>
);
}