Fragiola UI
Menus

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-menu
basic + submenu
Right-click here
Show 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 filecontext-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.

On this page