For the complete documentation index, see llms.txt. Markdown variants are available by appending .md to any URL or sending an Accept: text/markdown header. An agent skill is available at /.well-known/agent-skills/site-skill.md.
/
144
Sponsor

Context Menu

A right-click context menu with scale-from-corner animation.

report.pdf
Back
Reload
Save As…
Inspect

Installation

$ pnpm dlx shadcn@latest add @framecn/context-menu

Usage

import { ContextMenu } from "@/components/ui/context-menu";
<div style={{ position: "absolute", left: 400, top: 280 }}>
  <ContextMenu state="opened" />
</div>

Unlike dropdown menus, context menus have no built-in trigger — position the panel at the right-click point yourself.

Smooth transitions

The docs preview right-clicks a file card, opens the menu at the cursor, animates row 1 (Reload) hover → press → idle, then closes. Drive the panel with useContextMenuTransition, cursor motion with useCursorPath, and row states with useDropdownMenuItemTransition:

import { H, W } from "@/lib/customizer-config";
import { useCurrentState } from "@/lib/framecn-ui";
import { Cursor } from "@/components/framecn/cursor";
import { useCursorPath } from "@/components/framecn/use-cursor-path";
import { ContextMenu } from "@/components/framecn/context-menu";
import { useContextMenuTransition } from "@/components/framecn/use-context-menu-transition";
import { useDropdownMenuItemTransition } from "@/components/framecn/use-dropdown-menu-item-transition";
 
const CLICK_X = W / 2 + 20;
const CLICK_Y = H / 2 + 25;
 
const cursorStyle = useCursorPath([
  { at: 0, x: 80, y: 60 },
  { at: 30, x: CLICK_X, y: CLICK_Y, duration: 26 },
  { at: 42, x: CLICK_X, y: CLICK_Y, click: true, duration: 0 },
]);
 
const menuStyle = useContextMenuTransition([
  { at: 44, state: "opened", duration: 10 },
  { at: 92, state: "closed", duration: 10 },
]);
 
const rowState = useCurrentState(
  [
    { at: 60, state: "hover" },
    { at: 72, state: "press" },
    { at: 82, state: "idle" },
  ],
  "idle"
);
const rowStyle = useDropdownMenuItemTransition([{ at: 0, state: rowState }]);
 
<div style={{ position: "absolute", left: CLICK_X, top: CLICK_Y }}>
  <ContextMenu
    style={menuStyle}
    itemStyles={[undefined, rowStyle, undefined, undefined]}
  />
</div>
<Cursor style={cursorStyle} variant="pointer" />;

Schedule the menu's opened step 1–2 frames after the cursor's click: true waypoint so the ripple leads the reveal. style takes precedence over state when both are provided.

API Reference

ContextMenu

PropTypeDefault
state"opened" | "closed""closed"
styleContextMenuStyle- (takes precedence over state)
itemsstring[]["Back", "Reload", "Save As…", "Inspect"]
highlightedIndexnumber-1
pressedIndexnumber-1
itemStyles(DropdownMenuItemStyle | undefined)[]- (per-row override, indexed by row)
themePartial<FramecnTheme>-
classNamestring-