# Dropdown menu: Row actions

> One menu serves every row. Create a handle with `createDropdownMenuHandle`, pass it to each trigger with a `payload`, and read the payload in the menu. Delete hands off to an alert dialog.

Example: https://hextaui.com/docs/dropdown-menu/examples/row-actions
Component docs: https://hextaui.com/docs/dropdown-menu.md

## Add this example

```bash
npx shadcn@latest add https://hextaui.com/r/dropdown-menu-row-actions.json
```

This adds `components/examples/dropdown-menu/row-actions.tsx` with the Dropdown menu component and the HextaUI theme tokens.

## Source

```tsx title="components/examples/dropdown-menu/row-actions.tsx"
"use client"

import * as React from "react"
import { IconDots, IconPencil, 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 {
  createDropdownMenuHandle,
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"

const files = ["Quarterly report.pdf", "Brand assets", "Roadmap 2027.md"]

const rowMenu = createDropdownMenuHandle<{ name: string }>()

export function DropdownMenuRowActions() {
  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">
      <ul className="flex flex-col gap-0.5 rounded-xl border p-1">
        {files
          .filter((name) => !removed.includes(name))
          .map((name) => (
            <li
              key={name}
              className="flex items-center gap-3 rounded-lg py-1 ps-3 pe-1 text-sm"
            >
              <span className="min-w-0 flex-1 truncate">{name}</span>
              <DropdownMenuTrigger
                handle={rowMenu}
                payload={{ name }}
                aria-label={`Actions for ${name}`}
                render={<Button variant="ghost" size="icon-sm" />}
              >
                <IconDots />
              </DropdownMenuTrigger>
            </li>
          ))}
      </ul>
      <div>
        <Button variant="outline" size="sm" onClick={() => setRemoved([])}>
          Restore files
        </Button>
      </div>
      <DropdownMenu handle={rowMenu}>
        {({ payload }) => (
          <DropdownMenuContent align="end">
            <DropdownMenuItem>
              <IconPencil />
              Rename
            </DropdownMenuItem>
            <DropdownMenuSeparator />
            <DropdownMenuItem
              variant="destructive"
              onClick={() => setPending(payload?.name ?? null)}
            >
              <IconTrash />
              Delete…
            </DropdownMenuItem>
          </DropdownMenuContent>
        )}
      </DropdownMenu>
      <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>
  )
}
```

## Props in this example

### DropdownMenu

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `handle` | `DropdownMenuHandle<Payload>` | – | Connects detached triggers to this menu. |

### DropdownMenuTrigger

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `handle` | `DropdownMenuHandle<Payload>` | – |  |
| `payload` | `Payload` | – | Passed to the menu when this trigger opens it. |
| `render` | `ReactElement \| (props, state) => ReactElement` | `<button>` |  |

### DropdownMenuContent

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `align` | `"start" \| "center" \| "end"` | `"start"` |  |

### DropdownMenuItem

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `variant` | `"default" \| "destructive"` | `"default"` |  |
| `onClick` | `(event) => void` | – |  |

## More Dropdown menu examples

- [Safe area](https://hextaui.com/docs/dropdown-menu/examples/safe-area.md)
- [Checkboxes](https://hextaui.com/docs/dropdown-menu/examples/checkboxes.md)
- [Radio group](https://hextaui.com/docs/dropdown-menu/examples/radio-group.md)
- [Check on the right](https://hextaui.com/docs/dropdown-menu/examples/indicator-end.md)
- [Select-like](https://hextaui.com/docs/dropdown-menu/examples/select.md)
- [Controlled](https://hextaui.com/docs/dropdown-menu/examples/controlled.md)
- [Open on hover](https://hextaui.com/docs/dropdown-menu/examples/hover.md)
- [Disabled](https://hextaui.com/docs/dropdown-menu/examples/disabled.md)
- [Long content](https://hextaui.com/docs/dropdown-menu/examples/long-content.md)
- [Scrolling](https://hextaui.com/docs/dropdown-menu/examples/scrolling.md)
- [Inside a sheet](https://hextaui.com/docs/dropdown-menu/examples/in-sheet.md)
- [Right to left](https://hextaui.com/docs/dropdown-menu/examples/rtl.md)
