HextaUI

Context menu

Un menú de acciones con clic derecho o pulsación larga, con submenús, elementos de casilla y de radio, y respuesta al mantener pulsado en pantallas táctiles.

Right click here

Last action: Nothing yet

"use client"

import * as React from "react"

import {
  ContextMenu,
  ContextMenuCheckboxItem,
  ContextMenuContent,
  ContextMenuItem,
  ContextMenuLabel,
  ContextMenuRadioGroup,
  ContextMenuRadioItem,
  ContextMenuSeparator,
  ContextMenuShortcut,
  ContextMenuSub,
  ContextMenuSubContent,
  ContextMenuSubTrigger,
  ContextMenuTrigger,
} from "@/components/ui/context-menu"

export function ContextMenuDemo() {
  const [bookmarks, setBookmarks] = React.useState(true)
  const [urls, setUrls] = React.useState(false)
  const [person, setPerson] = React.useState("pedro")
  const [last, setLast] = React.useState("Nothing yet")

  return (
    <div className="flex w-full max-w-sm flex-col gap-2">
      <ContextMenu>
        <ContextMenuTrigger className="flex h-40 w-full items-center justify-center rounded-xl border border-dashed text-sm text-muted-foreground">
          Right click here
        </ContextMenuTrigger>
        <ContextMenuContent>
          <ContextMenuItem inset onClick={() => setLast("Back")}>
            Back
            <ContextMenuShortcut>⌘[</ContextMenuShortcut>
          </ContextMenuItem>
          <ContextMenuItem inset disabled>
            Forward
            <ContextMenuShortcut>⌘]</ContextMenuShortcut>
          </ContextMenuItem>
          <ContextMenuItem inset onClick={() => setLast("Reload")}>
            Reload
            <ContextMenuShortcut>⌘R</ContextMenuShortcut>
          </ContextMenuItem>
          <ContextMenuSub>
            <ContextMenuSubTrigger inset>More tools</ContextMenuSubTrigger>
            <ContextMenuSubContent>
              <ContextMenuItem onClick={() => setLast("Save page")}>
                Save page as…
                <ContextMenuShortcut>⇧⌘S</ContextMenuShortcut>
              </ContextMenuItem>
              <ContextMenuItem onClick={() => setLast("Create shortcut")}>
                Create shortcut…
              </ContextMenuItem>
              <ContextMenuItem onClick={() => setLast("Name window")}>
                Name window…
              </ContextMenuItem>
              <ContextMenuSeparator />
              <ContextMenuItem onClick={() => setLast("Developer tools")}>
                Developer tools
              </ContextMenuItem>
            </ContextMenuSubContent>
          </ContextMenuSub>
          <ContextMenuSeparator />
          <ContextMenuCheckboxItem
            checked={bookmarks}
            onCheckedChange={setBookmarks}
          >
            Show bookmarks bar
            <ContextMenuShortcut>⇧⌘B</ContextMenuShortcut>
          </ContextMenuCheckboxItem>
          <ContextMenuCheckboxItem checked={urls} onCheckedChange={setUrls}>
            Show full URLs
          </ContextMenuCheckboxItem>
          <ContextMenuSeparator />
          <ContextMenuRadioGroup value={person} onValueChange={setPerson}>
            <ContextMenuLabel inset>People</ContextMenuLabel>
            <ContextMenuRadioItem value="pedro">
              Pedro Duarte
            </ContextMenuRadioItem>
            <ContextMenuRadioItem value="colm">Colm Tuite</ContextMenuRadioItem>
          </ContextMenuRadioGroup>
        </ContextMenuContent>
      </ContextMenu>
      <p className="text-sm text-muted-foreground">Last action: {last}</p>
    </div>
  )
}
pnpm dlx shadcn@latest add https://hextaui.com/r/context-menu.json

Añade el componente, los tokens del tema de HextaUI y los componentes de HextaUI de los que depende.

import {
  ContextMenu,
  ContextMenuContent,
  ContextMenuItem,
  ContextMenuTrigger,
} from "@/components/ui/context-menu"
<ContextMenu>
  <ContextMenuTrigger>Right click here</ContextMenuTrigger>
  <ContextMenuContent>
    <ContextMenuItem>Profile</ContextMenuItem>
    <ContextMenuItem>Billing</ContextMenuItem>
    <ContextMenuItem variant="destructive">Delete</ContextMenuItem>
  </ContextMenuContent>
</ContextMenu>
ContextMenu
├── ContextMenuTrigger
└── ContextMenuContent
    ├── ContextMenuGroup
    │   ├── ContextMenuLabel
    │   └── ContextMenuItem
    │       └── ContextMenuShortcut
    ├── ContextMenuCheckboxItem
    ├── ContextMenuRadioGroup
    │   └── ContextMenuRadioItem
    ├── ContextMenuSeparator
    └── ContextMenuSub
        ├── ContextMenuSubTrigger
        └── ContextMenuSubContent

Lista de archivos

Dale a cada fila su propio menú. La fila abierta mantiene un resaltado, y un elemento destructivo pasa el relevo a un alert dialog para confirmar.

"use client"

import * as React from "react"
import {
  IconCopy,
  IconDownload,
  IconFile,
  IconFolder,
  IconLink,
  IconMail,
  IconPencil,
  IconRefresh,
  IconShare,
  IconStar,
  IconTrash,
} from "@tabler/icons-react"

import {
  AlertDialog,
  AlertDialogAction,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogTitle,
} from "@/components/ui/alert-dialog"
import { Button } from "@/components/ui/button"
import {
  ContextMenu,
  ContextMenuCheckboxItem,
  ContextMenuContent,
  ContextMenuItem,
  ContextMenuSeparator,
  ContextMenuShortcut,
  ContextMenuSub,
  ContextMenuSubContent,
  ContextMenuSubTrigger,
  ContextMenuTrigger,
} from "@/components/ui/context-menu"

const files = [
  { name: "Quarterly report.pdf", kind: "file" },
  { name: "Brand assets", kind: "folder" },
  { name: "Roadmap 2027.md", kind: "file" },
] as const

function FileRow({
  name,
  kind,
  onDelete,
}: {
  name: string
  kind: "file" | "folder"
  onDelete: () => void
}) {
  const [starred, setStarred] = React.useState(false)
  const Icon = kind === "folder" ? IconFolder : IconFile

  return (
    <ContextMenu>
      <ContextMenuTrigger className="flex items-center gap-3 rounded-lg px-3 py-2 text-sm hover:bg-muted/50 data-popup-open:bg-muted">
        <Icon className="size-4 text-muted-foreground" aria-hidden />
        <span className="min-w-0 flex-1 truncate">{name}</span>
        {starred ? (
          <IconStar
            className="size-4 text-muted-foreground"
            aria-label="Starred"
          />
        ) : null}
      </ContextMenuTrigger>
      <ContextMenuContent>
        <ContextMenuItem>
          <IconPencil />
          Rename…
        </ContextMenuItem>
        <ContextMenuItem>
          <IconCopy />
          Duplicate
          <ContextMenuShortcut>⌘D</ContextMenuShortcut>
        </ContextMenuItem>
        <ContextMenuItem>
          <IconDownload />
          Download
        </ContextMenuItem>
        <ContextMenuCheckboxItem checked={starred} onCheckedChange={setStarred}>
          Starred
        </ContextMenuCheckboxItem>
        <ContextMenuSub>
          <ContextMenuSubTrigger>
            <IconShare />
            Share
          </ContextMenuSubTrigger>
          <ContextMenuSubContent>
            <ContextMenuItem>
              <IconMail />
              Email
            </ContextMenuItem>
            <ContextMenuItem>
              <IconLink />
              Copy link
            </ContextMenuItem>
          </ContextMenuSubContent>
        </ContextMenuSub>
        <ContextMenuSeparator />
        <ContextMenuItem variant="destructive" onClick={onDelete}>
          <IconTrash />
          Delete
          <ContextMenuShortcut>⌘⌫</ContextMenuShortcut>
        </ContextMenuItem>
      </ContextMenuContent>
    </ContextMenu>
  )
}

export function ContextMenuFileList() {
  const [pending, setPending] = React.useState<string | null>(null)
  const [removed, setRemoved] = React.useState<string[]>([])

  return (
    <div className="flex w-full max-w-sm flex-col gap-3">
      <div className="flex flex-col gap-0.5 rounded-xl border p-1">
        {files
          .filter((file) => !removed.includes(file.name))
          .map((file) => (
            <FileRow
              key={file.name}
              name={file.name}
              kind={file.kind}
              onDelete={() => setPending(file.name)}
            />
          ))}
      </div>
      <div>
        <Button variant="outline" size="sm" onClick={() => setRemoved([])}>
          <IconRefresh />
          Restore files
        </Button>
      </div>
      <AlertDialog
        open={pending !== null}
        onOpenChange={(open) => {
          if (!open) {
            setPending(null)
          }
        }}
      >
        <AlertDialogContent size="sm">
          <AlertDialogHeader>
            <AlertDialogTitle>Delete “{pending}”?</AlertDialogTitle>
            <AlertDialogDescription>
              It moves to the trash for 30 days.
            </AlertDialogDescription>
          </AlertDialogHeader>
          <AlertDialogFooter>
            <AlertDialogCancel>Cancel</AlertDialogCancel>
            <AlertDialogAction
              variant="destructive"
              onClick={() => {
                if (pending) {
                  setRemoved((value) => [...value, pending])
                }
                setPending(null)
              }}
            >
              Delete
            </AlertDialogAction>
          </AlertDialogFooter>
        </AlertDialogContent>
      </AlertDialog>
    </div>
  )
}

Controlado

Pasa open y onOpenChange para controlar el estado. El segundo argumento indica por qué cambió, como trigger-press, outside-press o escape-key. Los elementos con closeOnClick={false} lo mantienen abierto.

"use client"

import * as React from "react"
import { IconCopy, IconScissors } from "@tabler/icons-react"

import {
  ContextMenu,
  ContextMenuContent,
  ContextMenuItem,
  ContextMenuTrigger,
} from "@/components/ui/context-menu"

export function ContextMenuControlled() {
  const [open, setOpen] = React.useState(false)
  const [reason, setReason] = React.useState("—")

  return (
    <div className="flex w-full max-w-sm flex-col gap-2">
      <ContextMenu
        open={open}
        onOpenChange={(next, details) => {
          setOpen(next)
          setReason(details.reason)
        }}
      >
        <ContextMenuTrigger className="flex h-40 w-full items-center justify-center rounded-xl border border-dashed text-sm text-muted-foreground">
          Controlled area
        </ContextMenuTrigger>
        <ContextMenuContent>
          <ContextMenuItem>
            <IconScissors />
            Cut
          </ContextMenuItem>
          <ContextMenuItem>
            <IconCopy />
            Copy
          </ContextMenuItem>
          <ContextMenuItem closeOnClick={false}>
            Stays open on click
          </ContextMenuItem>
        </ContextMenuContent>
      </ContextMenu>
      <p className="text-sm text-muted-foreground">
        Open: {String(open)} · last reason: {reason}
      </p>
    </div>
  )
}

Deshabilitado

Un <ContextMenu /> deshabilitado devuelve el área al menú propio del navegador. Los elementos deshabilitados siguen visibles pero el teclado los omite.

import { IconTrash } from "@tabler/icons-react"

import {
  ContextMenu,
  ContextMenuContent,
  ContextMenuItem,
  ContextMenuTrigger,
} from "@/components/ui/context-menu"

export function ContextMenuDisabled() {
  return (
    <div className="grid w-full max-w-lg gap-3 sm:grid-cols-2">
      <ContextMenu disabled>
        <ContextMenuTrigger className="flex h-40 w-full items-center justify-center rounded-xl border border-dashed text-sm text-muted-foreground">
          Disabled menu
        </ContextMenuTrigger>
        <ContextMenuContent>
          <ContextMenuItem>Never shown</ContextMenuItem>
        </ContextMenuContent>
      </ContextMenu>
      <ContextMenu>
        <ContextMenuTrigger className="flex h-40 w-full items-center justify-center rounded-xl border border-dashed text-sm text-muted-foreground">
          Disabled items
        </ContextMenuTrigger>
        <ContextMenuContent>
          <ContextMenuItem>Enabled</ContextMenuItem>
          <ContextMenuItem disabled>Disabled item</ContextMenuItem>
          <ContextMenuItem variant="destructive" disabled>
            <IconTrash />
            Disabled destructive
          </ContextMenuItem>
        </ContextMenuContent>
      </ContextMenu>
    </div>
  )
}

Feedback al mantener pulsado

En pantallas táctiles el menú se abre tras una pulsación larga. Mientras se mantiene el dedo, el área se encoge ligeramente para que se note que la pulsación se registró. Mover el dedo la cancela. Define holdFeedback={false} para desactivarlo.

import {
  ContextMenu,
  ContextMenuContent,
  ContextMenuItem,
  ContextMenuTrigger,
} from "@/components/ui/context-menu"

export function ContextMenuHoldFeedback() {
  return (
    <div className="grid w-full max-w-lg gap-3 sm:grid-cols-2">
      <ContextMenu>
        <ContextMenuTrigger className="flex h-40 w-full items-center justify-center rounded-xl border border-dashed text-sm text-muted-foreground">
          Press and hold
        </ContextMenuTrigger>
        <ContextMenuContent>
          <ContextMenuItem>Copy</ContextMenuItem>
          <ContextMenuItem>Paste</ContextMenuItem>
        </ContextMenuContent>
      </ContextMenu>
      <ContextMenu>
        <ContextMenuTrigger
          holdFeedback={false}
          className="flex h-40 w-full items-center justify-center rounded-xl border border-dashed text-sm text-muted-foreground"
        >
          No hold feedback
        </ContextMenuTrigger>
        <ContextMenuContent>
          <ContextMenuItem>Copy</ContextMenuItem>
          <ContextMenuItem>Paste</ContextMenuItem>
        </ContextMenuContent>
      </ContextMenu>
    </div>
  )
}

Contenido largo

Las etiquetas largas se ajustan dentro de un ancho máximo de 20rem, y los menús altos se desplazan dentro del espacio que queda en el viewport.

import { IconFile } from "@tabler/icons-react"

import {
  ContextMenu,
  ContextMenuContent,
  ContextMenuGroup,
  ContextMenuItem,
  ContextMenuLabel,
  ContextMenuShortcut,
  ContextMenuTrigger,
} from "@/components/ui/context-menu"

export function ContextMenuLongContent() {
  return (
    <div className="grid w-full max-w-lg gap-3 sm:grid-cols-2">
      <ContextMenu>
        <ContextMenuTrigger className="flex h-40 w-full items-center justify-center rounded-xl border border-dashed text-sm text-muted-foreground">
          Long labels
        </ContextMenuTrigger>
        <ContextMenuContent>
          <ContextMenuLabel>
            A label that goes on for much longer than any menu label should
          </ContextMenuLabel>
          <ContextMenuItem>
            <IconFile />
            Move to “Supercalifragilisticexpialidocious project archive 2026”
            <ContextMenuShortcut>⌥⌘M</ContextMenuShortcut>
          </ContextMenuItem>
          <ContextMenuItem>
            https://example.com/a/really/long/url/without/any/spaces/at/all/in/it
          </ContextMenuItem>
          <ContextMenuItem>مرحبا بالعالم — mixed العربية text</ContextMenuItem>
          <ContextMenuItem>👩‍👩‍👧‍👦 Family 日本語のテキスト</ContextMenuItem>
          <ContextMenuItem>Short</ContextMenuItem>
        </ContextMenuContent>
      </ContextMenu>
      <ContextMenu>
        <ContextMenuTrigger className="flex h-40 w-full items-center justify-center rounded-xl border border-dashed text-sm text-muted-foreground">
          40 items
        </ContextMenuTrigger>
        <ContextMenuContent>
          <ContextMenuGroup>
            <ContextMenuLabel>Recent</ContextMenuLabel>
            {Array.from({ length: 40 }, (_, index) => (
              <ContextMenuItem key={index}>
                Document {index + 1}
              </ContextMenuItem>
            ))}
          </ContextMenuGroup>
        </ContextMenuContent>
      </ContextMenu>
    </div>
  )
}

Submenús anidados

Los submenús se abren al pasar el cursor o con las teclas de flecha, a cualquier profundidad. El trigger de un submenú deshabilitado nunca se abre.

import {
  ContextMenu,
  ContextMenuContent,
  ContextMenuItem,
  ContextMenuSub,
  ContextMenuSubContent,
  ContextMenuSubTrigger,
  ContextMenuTrigger,
} from "@/components/ui/context-menu"

export function ContextMenuNested() {
  return (
    <ContextMenu>
      <ContextMenuTrigger className="flex h-40 w-full max-w-sm items-center justify-center rounded-xl border border-dashed text-sm text-muted-foreground">
        Three levels deep
      </ContextMenuTrigger>
      <ContextMenuContent>
        <ContextMenuItem>Open</ContextMenuItem>
        <ContextMenuSub>
          <ContextMenuSubTrigger>Move to</ContextMenuSubTrigger>
          <ContextMenuSubContent>
            <ContextMenuItem>Inbox</ContextMenuItem>
            <ContextMenuSub>
              <ContextMenuSubTrigger>Projects</ContextMenuSubTrigger>
              <ContextMenuSubContent>
                <ContextMenuItem>Acme</ContextMenuItem>
                <ContextMenuSub>
                  <ContextMenuSubTrigger>Archive</ContextMenuSubTrigger>
                  <ContextMenuSubContent>
                    <ContextMenuItem>2024</ContextMenuItem>
                    <ContextMenuItem>2025</ContextMenuItem>
                  </ContextMenuSubContent>
                </ContextMenuSub>
              </ContextMenuSubContent>
            </ContextMenuSub>
            <ContextMenuSub disabled>
              <ContextMenuSubTrigger disabled>Locked</ContextMenuSubTrigger>
              <ContextMenuSubContent>
                <ContextMenuItem>Hidden</ContextMenuItem>
              </ContextMenuSubContent>
            </ContextMenuSub>
          </ContextMenuSubContent>
        </ContextMenuSub>
        <ContextMenuItem variant="destructive">Delete</ContextMenuItem>
      </ContextMenuContent>
    </ContextMenu>
  )
}

Dentro de una hoja

El menú se apila sobre otras superposiciones, y Escape cierra solo el menú, no el sheet que hay detrás.

import { Button } from "@/components/ui/button"
import {
  ContextMenu,
  ContextMenuContent,
  ContextMenuItem,
  ContextMenuTrigger,
} from "@/components/ui/context-menu"
import {
  Sheet,
  SheetBody,
  SheetContent,
  SheetDescription,
  SheetHeader,
  SheetTitle,
  SheetTrigger,
} from "@/components/ui/sheet"

export function ContextMenuInSheet() {
  return (
    <Sheet>
      <SheetTrigger render={<Button variant="outline" />}>
        Open sheet
      </SheetTrigger>
      <SheetContent>
        <SheetHeader>
          <SheetTitle>Inside a sheet</SheetTitle>
          <SheetDescription>
            The menu layers above the sheet and Escape closes only the menu.
          </SheetDescription>
        </SheetHeader>
        <SheetBody>
          <ContextMenu>
            <ContextMenuTrigger className="flex h-40 w-full items-center justify-center rounded-xl border border-dashed text-sm text-muted-foreground">
              Right click here
            </ContextMenuTrigger>
            <ContextMenuContent>
              <ContextMenuItem>Copy</ContextMenuItem>
              <ContextMenuItem>Paste</ContextMenuItem>
            </ContextMenuContent>
          </ContextMenu>
        </SheetBody>
      </SheetContent>
    </Sheet>
  )
}

Renderizar como otro elemento

Usa render para convertir el trigger en cualquier elemento, como una figure, o para convertir un elemento en un enlace.

import { IconDownload, IconLink } from "@tabler/icons-react"

import {
  ContextMenu,
  ContextMenuContent,
  ContextMenuItem,
  ContextMenuTrigger,
} from "@/components/ui/context-menu"

export function ContextMenuRender() {
  return (
    <ContextMenu>
      <ContextMenuTrigger
        render={<figure />}
        className="flex w-full max-w-xs flex-col gap-2 rounded-xl border p-3"
      >
        <div className="h-28 rounded-lg bg-muted" />
        <figcaption className="text-sm text-muted-foreground">
          Trigger rendered as a figure
        </figcaption>
      </ContextMenuTrigger>
      <ContextMenuContent>
        <ContextMenuItem>
          <IconDownload />
          Save image
        </ContextMenuItem>
        <ContextMenuItem render={<a href="#render-as-another-element" />}>
          <IconLink />
          Link item
        </ContextMenuItem>
      </ContextMenuContent>
    </ContextMenu>
  )
}

De derecha a izquierda

El menú lee la dirección del trigger, así que los submenús se abren hacia la izquierda y las teclas de flecha se invierten.

import { IconArrowLeft, IconArrowRight, IconTrash } from "@tabler/icons-react"

import {
  ContextMenu,
  ContextMenuCheckboxItem,
  ContextMenuContent,
  ContextMenuItem,
  ContextMenuSeparator,
  ContextMenuShortcut,
  ContextMenuSub,
  ContextMenuSubContent,
  ContextMenuSubTrigger,
  ContextMenuTrigger,
} from "@/components/ui/context-menu"

export function ContextMenuRtl() {
  return (
    <div dir="rtl" className="w-full max-w-sm">
      <ContextMenu>
        <ContextMenuTrigger className="flex h-40 w-full items-center justify-center rounded-xl border border-dashed text-sm text-muted-foreground">
          انقر بزر الفأرة الأيمن هنا
        </ContextMenuTrigger>
        <ContextMenuContent>
          <ContextMenuItem>
            <IconArrowRight />
            رجوع
            <ContextMenuShortcut>⌘[</ContextMenuShortcut>
          </ContextMenuItem>
          <ContextMenuItem>
            <IconArrowLeft />
            تقدم
          </ContextMenuItem>
          <ContextMenuCheckboxItem defaultChecked>
            إظهار الشريط
          </ContextMenuCheckboxItem>
          <ContextMenuSub>
            <ContextMenuSubTrigger>مشاركة</ContextMenuSubTrigger>
            <ContextMenuSubContent>
              <ContextMenuItem>بريد</ContextMenuItem>
              <ContextMenuItem>نسخ الرابط</ContextMenuItem>
            </ContextMenuSubContent>
          </ContextMenuSub>
          <ContextMenuSeparator />
          <ContextMenuItem variant="destructive">
            <IconTrash />
            حذف
          </ContextMenuItem>
        </ContextMenuContent>
      </ContextMenu>
    </div>
  )
}
KeyAcción
↓Resalta el elemento siguiente, volviendo al principio al llegar al final.
↑Resalta el elemento anterior, volviendo al final al llegar al principio.
HomeResalta el primer elemento.
EndResalta el último elemento.
EnterSpaceEjecuta el elemento resaltado. Los elementos checkbox y radio alternan y mantienen el menú abierto.
→Abre el submenú resaltado y entra en él. ← en diseños de derecha a izquierda.
←Cierra el submenú actual y vuelve a su trigger. → en diseños de derecha a izquierda.
EscCierra el menú actual. En un submenú, solo se cierra ese submenú.
A–ZResalta el siguiente elemento que empiece por esa letra.
  • Un menú contextual es un atajo. Haz que cada acción que contiene sea accesible también de otra forma, como un botón visible o un menú desplegable, ya que muchas personas nunca hacen clic derecho ni una pulsación larga.
  • Los navegadores también disparan el evento de menú contextual con Shift F10 y la tecla Menu en un elemento con foco, así que un elemento enfocable dentro del trigger permite abrirlo a los usuarios de teclado.
  • Los atajos de <ContextMenuShortcut /> son solo etiquetas. Enlaza las teclas tú mismo.
  • Con movimiento reducido, se omiten el feedback al mantener pulsado y el parpadeo del elemento, y el menú solo se desvanece.

Construido sobre el context menu de Base UI. Cada parte acepta las props de la primitiva que envuelve, y un className en forma de función que recibe el estado de la parte.

PropTipoPredeterminado
open
boolean–
defaultOpen
booleanfalse
onOpenChangedetails.reason indica qué causó el cambio.
(open: boolean, details) => void–
onOpenChangeCompleteSe ejecuta cuando termina la animación de apertura o cierre.
(open: boolean) => void–
disabledMuestra en su lugar el menú nativo del navegador.
booleanfalse
loopFocusDa la vuelta a la navegación con teclas de flecha en los extremos.
booleantrue
highlightItemOnHover
booleantrue
actionsRefCierra el menú de forma imperativa.
RefObject<{ close, unmount }>–
PropTipoPredeterminado
holdFeedbackEncoge ligeramente el área mientras se mantiene una pulsación larga en pantallas táctiles.
booleantrue
render
ReactElement | (props, state) => ReactElement<div>
AtributoDescripción
data-slot="context-menu-trigger"Apunta al trigger en CSS.
data-popup-openPresente mientras su menú está abierto.
data-holdingPresente mientras se mantiene una pulsación larga.
data-pressedPresente mientras el trigger está pulsado.
PropTipoPredeterminado
sideLado preferido, relativo al puntero.
"top" | "right" | "bottom" | "left" | "inline-start" | "inline-end""bottom"
align
"start" | "center" | "end""start"
sideOffset
number0
alignOffset
number0
collisionPaddingEspacio que se mantiene entre el menú y los bordes del viewport.
number | { top, right, bottom, left }–
collisionAvoidanceCómo se voltea o desplaza el menú cuando se desbordaría.
CollisionAvoidance–
anchorPosiciona respecto a algo distinto del puntero.
Element | VirtualElement | RefObject–
finalFocusAdónde va el foco después de cerrarse el menú.
boolean | RefObject | (closeType) => HTMLElement | boolean–
render
ReactElement | (props, state) => ReactElement<div>
AtributoDescripción
data-slot="context-menu-content"El popup del menú.
data-openPresente mientras está abierto.
data-starting-stylePresente mientras el menú se anima al abrirse.
data-ending-stylePresente mientras el menú se anima al cerrarse.
data-sideEl lado en el que se colocó tras las colisiones.
data-chosenPresente tras hacer clic en un elemento. El fundido de salida espera al parpadeo.
--transform-originEl punto desde el que crece la animación de escala.
--available-heightEspacio que queda en el viewport. Limita la altura del menú.
PropTipoPredeterminado
variant
"default" | "destructive""default"
insetSangra la etiqueta para alinearla con los elementos checkbox.
booleanfalse
onClickSe ejecuta al hacer clic, con Enter o con Space. El menú se cierra tras un breve parpadeo.
(event) => void–
closeOnClick
booleantrue
disabled
booleanfalse
labelTexto usado para la búsqueda por escritura cuando los hijos no son texto simple.
string–
render
ReactElement | (props, state) => ReactElement<div>
AtributoDescripción
data-slot="context-menu-item"Apunta a los elementos en CSS.
data-variantLa variante actual.
data-highlightedPresente en el elemento bajo el puntero o el foco del teclado.
data-disabledPresente cuando el elemento está deshabilitado.
data-insetPresente cuando se define inset.
data-chosenPresente en el elemento clicado mientras parpadea.
PropTipoPredeterminado
checked
boolean–
defaultChecked
booleanfalse
onCheckedChange
(checked: boolean, details) => void–
closeOnClick
booleanfalse
inset
booleanfalse
disabled
booleanfalse
render
ReactElement | (props, state) => ReactElement<div>
AtributoDescripción
data-slot="context-menu-checkbox-item"Selecciona los elementos checkbox en CSS.
data-checkedPresente cuando está marcado.
data-uncheckedPresente cuando está desmarcado.
data-highlightedPresente en el elemento bajo el puntero o el foco del teclado.
data-disabledPresente cuando el elemento está deshabilitado.
data-insetPresente cuando se define inset.
data-chosenPresente en el elemento clicado mientras parpadea.
PropTipoPredeterminado
value
any–
defaultValue
any–
onValueChange
(value: any, details) => void–
disabled
booleanfalse
PropTipoPredeterminado
value
any–
closeOnClick
booleanfalse
inset
booleanfalse
disabled
booleanfalse
render
ReactElement | (props, state) => ReactElement<div>
AtributoDescripción
data-slot="context-menu-radio-item"Selecciona los elementos radio en CSS.
data-checkedPresente cuando está seleccionado.
data-highlightedPresente en el elemento bajo el puntero o el foco del teclado.
data-disabledPresente cuando el elemento está deshabilitado.
data-insetPresente cuando se define inset.
data-chosenPresente en el elemento clicado mientras parpadea.
PropTipoPredeterminado
inset
booleanfalse
render
ReactElement | (props, state) => ReactElement<div>

Dentro de <ContextMenuGroup /> o <ContextMenuRadioGroup /> etiqueta el grupo para las tecnologías de asistencia. En otros sitios es un encabezado simple.

PropTipoPredeterminado
open
boolean–
defaultOpen
booleanfalse
onOpenChange
(open: boolean, details) => void–
disabled
booleanfalse
closeParentOnEscCierra todo el menú con Escape, no solo este submenú.
booleanfalse
PropTipoPredeterminado
inset
booleanfalse
openOnHover
booleantrue
delayMilisegundos de hover antes de que se abra el submenú.
number100
closeDelay
number0
disabled
booleanfalse
render
ReactElement | (props, state) => ReactElement<div>
AtributoDescripción
data-slot="context-menu-sub-trigger"Selecciona los triggers de submenú en CSS.
data-popup-openPresente mientras su submenú está abierto.
data-highlightedPresente mientras está resaltado.
data-disabledPresente cuando está deshabilitado.
PropTipoPredeterminado
sideSe abre hacia el final en línea por defecto.
"top" | "right" | "bottom" | "left" | "inline-start" | "inline-end"–
align
"start" | "center" | "end"–
sideOffset
number0
alignOffset
number-4
collisionPaddingEspacio que se mantiene entre el menú y los bordes del viewport.
number | { top, right, bottom, left }–
collisionAvoidanceCómo se voltea o desplaza el menú cuando se desbordaría.
CollisionAvoidance–
anchorPosiciona respecto a algo distinto del puntero.
Element | VirtualElement | RefObject–
render
ReactElement | (props, state) => ReactElement<div>
AtributoDescripción
data-slot="context-menu-sub-content"El popup del submenú. Acepta los mismos atributos de estado que el menú.
PropTipoPredeterminado
dirMantiene en orden atajos como ⇧⌘S dentro de menús de derecha a izquierda.
"ltr" | "rtl""ltr"
AtributoDescripción
data-slot="context-menu-shortcut"La etiqueta del atajo.

<ContextMenuGroup /> agrupa elementos relacionados bajo una etiqueta. <ContextMenuSeparator /> dibuja un divisor. Ambos aceptan render y className, y llevan context-menu-group y context-menu-separator como su data-slot.

Usado en bloques

Bloques que se construyen sobre Context menu.