Context Menu
A menu of actions triggered by a right-click. Shares the `popup` and `menu` style families with dropdown-menu — installing it after `dropdown-menu` writes exactly one file.
npx shadcn@latest add @fragiola/context-menuShow code
"use client";
import type { ReactNode } from "react";
import { ContextMenu } from "#/ui/context-menu";
// The floor is palette-surface. The context menu trigger is a dashed surface
// that reads roles from the floor; the menu content travels through a portal
// and inherits the palette from the owning subtree. The destructive item
// carries palette-danger explicitly — the one chromatic exception for
// destructive menu items.
export default function ContextMenuDemo() {
return (
<div className="palette-surface flex flex-col gap-6 rounded-lg border border-palette-line bg-palette-base p-6">
<Row label="basic + submenu">
<ContextMenu.Root>
<ContextMenu.Trigger
render={
<div className="flex h-32 w-64 items-center justify-center rounded-md border border-dashed border-palette-line bg-palette-soft text-sm text-palette-accent/85" />
}
>
Right-click here
</ContextMenu.Trigger>
<ContextMenu.Content>
<ContextMenu.Item>
<span>Back</span>
</ContextMenu.Item>
<ContextMenu.Item inset disabled>
<span>Forward</span>
</ContextMenu.Item>
<ContextMenu.Item inset>
<span>Reload</span>
</ContextMenu.Item>
<ContextMenu.Separator />
<ContextMenu.Sub>
<ContextMenu.SubTrigger>
<span>More tools</span>
</ContextMenu.SubTrigger>
<ContextMenu.SubContent>
<ContextMenu.Item>
<span>Save page as…</span>
</ContextMenu.Item>
<ContextMenu.Item>
<span>Print…</span>
</ContextMenu.Item>
</ContextMenu.SubContent>
</ContextMenu.Sub>
<ContextMenu.Separator />
<ContextMenu.CheckboxItem checked>
<span>Show bookmarks bar</span>
</ContextMenu.CheckboxItem>
<ContextMenu.Separator />
{/* Destructive item — a palette class, not a prop */}
<ContextMenu.Item className="palette-danger">
<span>Delete</span>
</ContextMenu.Item>
</ContextMenu.Content>
</ContextMenu.Root>
</Row>
</div>
);
}
function Row({ label, children }: { label: string; children: ReactNode }) {
return (
<div className="flex flex-col gap-2">
<span className="text-xs font-mono text-palette-accent/85">
{label}
</span>
{children}
</div>
);
}
Style families
context-menu renders from the same menu source as dropdown-menu. The only differences are Root, Trigger (which carries select-none — behavioural, not visual, so it does not come from a family) and the positioning defaults.
The select-none on ContextMenu.Trigger stops text selection on right-click. It is behavioural, not visual, so it does not come from a family — keep the distinction.
Distribution proof
Installing dropdown-menu pulls menu and popup. Installing context-menu afterwards writes one file — context-menu.tsx itself. The shared families are already on disk. This is the distribution-level proof that the duplication claim holds.
Parts
Identical to dropdown-menu — same factory, same members. See the dropdown-menu parts table.
Dropdown Menu
A menu of actions triggered by a button. Shares the `popup` and `menu` style families with context-menu, select and combobox — the only things this component declares for itself are `Root`, `Trigger` and its positioning defaults.
Dialog
A modal layer — a centred panel over a scrim. Shares the `layer` family with alert-dialog and drawer; the only things this component declares for itself are `Root`, `Trigger`, `Portal` and the centring transform.