Dropdown Menu
Menu de ações liderado por verbos, atrelado a um trigger. Itens, grupos, separadores, itens de checkbox ou radio, submenus e dicas de shortcut alinhadas à direita.
Visão geral
DropdownMenu é o primitivo certo para ações: Edit, Delete, Duplicate, Open in new tab. É distinto do Select, que guarda valores (USA, Brazil, Japan). Quando os itens do menu se leem como verbos, este é o primitivo certo.
O compound expõe tudo o que o Radix oferece: itens com variante destructive, itens de checkbox e radio com indicadores auto-renderizados, seções agrupadas, separadores, submenus e um helper de shortcut que alinha o texto à direita.
Preview
'use client';import { Button } from '@gremorie/rx-forms';import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger,} from '@gremorie/rx-overlays';export function DropdownMenuPreview() { return ( <DropdownMenu> <DropdownMenuTrigger asChild> <Button variant="outline">Actions</Button> </DropdownMenuTrigger> <DropdownMenuContent> <DropdownMenuLabel>Actions</DropdownMenuLabel> <DropdownMenuSeparator /> <DropdownMenuItem>Open in chat</DropdownMenuItem> <DropdownMenuItem>Copy URL</DropdownMenuItem> <DropdownMenuItem>Export</DropdownMenuItem> </DropdownMenuContent> </DropdownMenu> );}Anatomia
DropdownMenu Raiz que mantém o estado de abertura
├─ DropdownMenuTrigger Elemento que abre o menu
└─ DropdownMenuContent Superfície em portal (sideOffset default 4)
├─ DropdownMenuLabel Rótulo de seção não interativo
├─ DropdownMenuSeparator Divisor
├─ DropdownMenuGroup Agrupa itens relacionados
│ └─ DropdownMenuItem Linha de ação (variant default | destructive, inset)
├─ DropdownMenuCheckboxItem Item alternável
├─ DropdownMenuRadioGroup Container de escolha única
│ └─ DropdownMenuRadioItem Item de escolha única
├─ DropdownMenuSub Submenu aninhado
│ ├─ DropdownMenuSubTrigger Abre o submenu
│ └─ DropdownMenuSubContent Superfície do submenu
└─ DropdownMenuShortcut Dica de teclado alinhada à direitaInstalação
bash npx gremorie@latest add rx-dropdown-menu bash pnpm dlx gremorie@latest add rx-dropdown-menu
bash yarn dlx gremorie@latest add rx-dropdown-menu
bash bunx --bun gremorie@latest add rx-dropdown-menu
Uso
import { Button } from "@gremorie/rx-forms";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@gremorie/rx-overlays";
export function Example() {
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline">Actions</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuLabel>Actions</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem>Open in chat</DropdownMenuItem>
<DropdownMenuItem>Copy URL</DropdownMenuItem>
<DropdownMenuItem>Export</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}A edição Angular deste componente hoje é distribuída a partir do source (veja o workbench para o lado a lado); a entrada no registry vem a seguir.
API
<DropdownMenu>
Estende o Radix DropdownMenu.Root. API controlada padrão (open, defaultOpen, onOpenChange, modal).
<DropdownMenuTrigger>
Estende o Radix DropdownMenu.Trigger. Use asChild para encaminhar estilos a um Button ou qualquer trigger customizado.
<DropdownMenuContent>
| Prop | Type | Default | Description |
|---|---|---|---|
sideOffset | number | 4 | Espaçamento em pixels entre trigger e content. |
side | "top" | "right" | "bottom" | "left" | "bottom" | Lado preferido; vira automaticamente. |
align | "start" | "center" | "end" | "center" | Alinhamento relativo ao eixo do trigger. |
Envolvido em um Portal do Radix. Limitado a --radix-dropdown-menu-content-available-height, com scroll, min-w-[8rem].
<DropdownMenuItem>
| Prop | Type | Default | Description |
|---|---|---|---|
inset | boolean | false | Adiciona pl-8 para o item alinhar com as linhas de checkbox/radio. |
variant | "default" | "destructive" | "default" | destructive troca a cor do texto e do ícone para o token destructive. |
disabled | boolean | false | Desabilita a interação; renderiza a 50% de opacidade. |
<DropdownMenuCheckboxItem>
| Prop | Type | Default | Description |
|---|---|---|---|
checked | boolean | "indeterminate" | - | Estado checked controlado. |
onCheckedChange | (checked: boolean) => void | - | Disparado ao alternar. |
Renderiza um ícone de check posicionado de forma absoluta à esquerda quando marcado.
<DropdownMenuRadioGroup> + <DropdownMenuRadioItem>
Emparelhe um grupo com um ou mais itens de radio. O grupo detém o value e o onValueChange. Cada item de radio renderiza um bullet auto-posicionado quando selecionado.
<DropdownMenuLabel>
Heading de seção não interativo.
| Prop | Type | Default | Description |
|---|---|---|---|
inset | boolean | false | Adiciona pl-8 para alinhar com as linhas de check/radio. |
<DropdownMenuSeparator>
Divisor fino bg-border com margem negativa para abranger o padding do menu.
<DropdownMenuShortcut>
Um <span> alinhado à direita para dicas de teclado (⌘C, ⇧⌫). Helper de layout puro; sem lógica Radix.
<DropdownMenuSub> + <DropdownMenuSubTrigger> + <DropdownMenuSubContent>
Submenu aninhado. O sub-trigger renderiza um chevron auto-posicionado à direita. O sub-content abre ao lado do trigger.
<DropdownMenuPortal>, <DropdownMenuGroup>
Pass-throughs sobre os primitivos do Radix com atributos data-slot.
Composição
<DropdownMenu>detém o estado de aberto/fechado.<DropdownMenuTrigger asChild>envolve umButtonou botão de ícone.<DropdownMenuContent>monta via Portal ancorado ao trigger.- Dentro do content: agrupe itens com
DropdownMenuGroup, rotule seções comDropdownMenuLabel, divida comDropdownMenuSeparator. - Shortcuts ficam na borda direita via
DropdownMenuShortcut. - Submenus envolvem blocos aninhados de
DropdownMenuSub.
Variações
Submenu com shortcuts e ação destrutiva
O menu canônico de ações de linha: shortcuts alinhados à direita, um DropdownMenuSub aninhado e a variante destructive para delete.
'use client';import { CopyIcon, MoreHorizontalIcon, PencilIcon, Trash2Icon,} from 'lucide-react';import { Button } from '@gremorie/rx-forms';import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger,} from '@gremorie/rx-overlays';export function DropdownMenuSubmenuPreview() { return ( <DropdownMenu> <DropdownMenuTrigger asChild> <Button variant="ghost" size="icon" aria-label="Row actions"> <MoreHorizontalIcon /> </Button> </DropdownMenuTrigger> <DropdownMenuContent align="end" className="w-48"> <DropdownMenuItem> <PencilIcon /> Edit <DropdownMenuShortcut>⌘E</DropdownMenuShortcut> </DropdownMenuItem> <DropdownMenuItem> <CopyIcon /> Duplicate <DropdownMenuShortcut>⌘D</DropdownMenuShortcut> </DropdownMenuItem> <DropdownMenuSub> <DropdownMenuSubTrigger>Share</DropdownMenuSubTrigger> <DropdownMenuSubContent> <DropdownMenuItem>Copy link</DropdownMenuItem> <DropdownMenuItem>Email</DropdownMenuItem> <DropdownMenuItem>Embed</DropdownMenuItem> </DropdownMenuSubContent> </DropdownMenuSub> <DropdownMenuSeparator /> <DropdownMenuItem variant="destructive"> <Trash2Icon /> Delete <DropdownMenuShortcut>⌫</DropdownMenuShortcut> </DropdownMenuItem> </DropdownMenuContent> </DropdownMenu> );}Itens de checkbox
Alterne colunas ou filtros visíveis sem fechar o menu. Cada DropdownMenuCheckboxItem renderiza seu próprio indicador de check.
'use client';import { useState } from 'react';import { Button } from '@gremorie/rx-forms';import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger,} from '@gremorie/rx-overlays';export function DropdownMenuCheckboxesPreview() { const [columns, setColumns] = useState({ name: true, email: true, role: false, }); return ( <DropdownMenu> <DropdownMenuTrigger asChild> <Button variant="outline">Toggle columns</Button> </DropdownMenuTrigger> <DropdownMenuContent className="w-48"> <DropdownMenuLabel>Visible columns</DropdownMenuLabel> <DropdownMenuSeparator /> <DropdownMenuCheckboxItem checked={columns.name} onCheckedChange={(checked) => setColumns((prev) => ({ ...prev, name: checked === true })) } > Name </DropdownMenuCheckboxItem> <DropdownMenuCheckboxItem checked={columns.email} onCheckedChange={(checked) => setColumns((prev) => ({ ...prev, email: checked === true })) } > Email </DropdownMenuCheckboxItem> <DropdownMenuCheckboxItem checked={columns.role} onCheckedChange={(checked) => setColumns((prev) => ({ ...prev, role: checked === true })) } > Role </DropdownMenuCheckboxItem> </DropdownMenuContent> </DropdownMenu> );}Itens de radio
Escolha um único valor de um DropdownMenuRadioGroup. O item selecionado renderiza um bullet auto-posicionado.
'use client';import { useState } from 'react';import { Button } from '@gremorie/rx-forms';import { DropdownMenu, DropdownMenuContent, DropdownMenuLabel, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuTrigger,} from '@gremorie/rx-overlays';export function DropdownMenuRadioPreview() { const [position, setPosition] = useState('bottom'); return ( <DropdownMenu> <DropdownMenuTrigger asChild> <Button variant="outline">Panel position</Button> </DropdownMenuTrigger> <DropdownMenuContent className="w-48"> <DropdownMenuLabel>Panel position</DropdownMenuLabel> <DropdownMenuSeparator /> <DropdownMenuRadioGroup value={position} onValueChange={setPosition}> <DropdownMenuRadioItem value="top">Top</DropdownMenuRadioItem> <DropdownMenuRadioItem value="bottom">Bottom</DropdownMenuRadioItem> <DropdownMenuRadioItem value="right">Right</DropdownMenuRadioItem> </DropdownMenuRadioGroup> </DropdownMenuContent> </DropdownMenu> );}Itens com shortcuts e ação destrutiva
O menu canônico de ações de linha. Note os shortcuts alinhados à direita e a variante destructive para delete.
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="icon" aria-label="Row actions">
<MoreHorizontalIcon />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem>
<PencilIcon /> Edit
<DropdownMenuShortcut>⌘E</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuItem>
<CopyIcon /> Duplicate
<DropdownMenuShortcut>⌘D</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem variant="destructive">
<Trash2Icon /> Delete
<DropdownMenuShortcut>⌫</DropdownMenuShortcut>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>Itens de checkbox
Alterne colunas ou filtros visíveis sem fechar o menu.
<DropdownMenuContent>
<DropdownMenuLabel>Toggle columns</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuCheckboxItem
checked={cols.name}
onCheckedChange={toggle('name')}
>
Name
</DropdownMenuCheckboxItem>
<DropdownMenuCheckboxItem
checked={cols.email}
onCheckedChange={toggle('email')}
>
Email
</DropdownMenuCheckboxItem>
</DropdownMenuContent>Submenu
Aninhe ações secundárias dentro de um sub-trigger.
<DropdownMenuContent>
<DropdownMenuItem>Open</DropdownMenuItem>
<DropdownMenuSub>
<DropdownMenuSubTrigger>Share</DropdownMenuSubTrigger>
<DropdownMenuSubContent>
<DropdownMenuItem>Copy link</DropdownMenuItem>
<DropdownMenuItem>Email</DropdownMenuItem>
<DropdownMenuItem>Embed</DropdownMenuItem>
</DropdownMenuSubContent>
</DropdownMenuSub>
</DropdownMenuContent>Acessibilidade
- Role:
role="menu"no content,role="menuitem"nos itens,role="menuitemcheckbox"erole="menuitemradio"nas variantes com estado. - Teclado: as setas navegam;
EntereSpaceativam; o type-ahead salta para itens que começam com a letra digitada;Escfecha;Tabfecha e continua o foco na página. - Submenus:
→abre,←fecha. - Gestão de foco: o foco entra no menu na abertura e retorna ao trigger ao fechar.
aria-expandedé definido no trigger automaticamente;aria-controlsliga ao id do content.- Variante destructive: apenas dica visual; emparelhe com confirmação (
AlertDialog) se a ação for irreversível. - Reduced motion: as animações de abrir/fechar respeitam
prefers-reduced-motion.
Relacionados
- Context Menu - mesmo compound, acionado por clique direito em uma região.
- Select - picker liderado por valores (países, status), não por ações.
- Popover - overlay ancorado genérico quando os itens não seguem a semântica de menu.
- Command - lista de comandos buscável para usuários avançados.