# DropdownMenu

> A menu of actions or options that opens from a trigger button, with full keyboard navigation, submenus and real DOM focus on every item.

- Package: `fancy-ui-svelte` (Svelte 5 + Tailwind CSS v4)
- Category: Navigation
- Tags: menu, dropdown, actions, compound, submenu, accessibility
- Docs: https://fancy-ui.rama.app/docs/components/dropdown-menu
- Source: https://github.com/RamaHerbin/fancy-ui/tree/main/src/lib/fancy-ui/dropdown-menu

## Import

```svelte
<script lang="ts">
  import { DropdownMenu } from 'fancy-ui-svelte';
</script>
```

Setup (once per project): `npm install fancy-ui-svelte`, then add `@import "fancy-ui-svelte/tailwind.css";` after `@import "tailwindcss";` in your main CSS file. Full guide: https://fancy-ui.rama.app/llms.txt

React: `import { DropdownMenu } from 'fancy-ui-react';` (same prop names; `class` becomes `className`, snippets become `ReactNode`).

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `open` | `boolean` | `false` | Whether the menu is open. Bindable |
| `onOpenChange` | `(open: boolean) => void` | — | Called whenever the menu opens or closes, however it happened |
| `side` | `"top" \| "bottom" \| "left" \| "right"` | `"bottom"` | Side of the trigger to place the menu on. Flips when it would overflow |
| `align` | `"start" \| "center" \| "end"` | `"start"` | Alignment along the trigger's cross axis |
| `offset` | `number` | `4` | Gap in pixels between the trigger and the menu |
| `loop` | `boolean` | `true` | Whether arrow-key navigation wraps at the ends |
| `sound` | `boolean` | `false` | Plays open and close on the trigger and submenus, and select on an item — one cue per interaction — once the user has enabled sound |

## Snippets

| Snippet | Description |
| --- | --- |
| `children` | The DropdownMenuTrigger and DropdownMenuContent |

## Examples

### Basic Usage

```svelte
<script lang="ts">
	import {
		DropdownMenu,
		DropdownMenuTrigger,
		DropdownMenuContent,
		DropdownMenuItem,
		DropdownMenuSeparator,
	} from "fancy-ui-svelte";
</script>

<DropdownMenu sound>
	<DropdownMenuTrigger>Options</DropdownMenuTrigger>
	<DropdownMenuContent>
		<DropdownMenuItem onSelect={() => console.log("rename")}>Rename</DropdownMenuItem>
		<DropdownMenuItem onSelect={() => console.log("duplicate")}>Duplicate</DropdownMenuItem>
		<DropdownMenuItem onSelect={() => console.log("share")}>Share…</DropdownMenuItem>
		<DropdownMenuSeparator />
		<DropdownMenuItem variant="destructive" onSelect={() => console.log("delete")}>
			Delete
		</DropdownMenuItem>
	</DropdownMenuContent>
</DropdownMenu>
```

### With Shortcuts

Display-only keyboard shortcuts rendered as trailing kbd tags.

```svelte
<script lang="ts">
	import {
		DropdownMenu,
		DropdownMenuTrigger,
		DropdownMenuContent,
		DropdownMenuItem,
		DropdownMenuSeparator,
	} from "fancy-ui-svelte";
</script>

<DropdownMenu sound>
	<DropdownMenuTrigger>File</DropdownMenuTrigger>
	<DropdownMenuContent>
		<DropdownMenuItem shortcut="⌘R" onSelect={() => console.log("rename")}>Rename</DropdownMenuItem>
		<DropdownMenuItem shortcut="⌘D" onSelect={() => console.log("duplicate")}>
			Duplicate
		</DropdownMenuItem>
		<DropdownMenuSeparator />
		<DropdownMenuItem shortcut="⌫" onSelect={() => console.log("delete")}>Delete</DropdownMenuItem>
	</DropdownMenuContent>
</DropdownMenu>
```

### Destructive Item

A destructive action set apart with the destructive variant.

```svelte
<script lang="ts">
	import {
		DropdownMenu,
		DropdownMenuTrigger,
		DropdownMenuContent,
		DropdownMenuItem,
		DropdownMenuSeparator,
	} from "fancy-ui-svelte";
</script>

<DropdownMenu sound>
	<DropdownMenuTrigger>Project</DropdownMenuTrigger>
	<DropdownMenuContent>
		<DropdownMenuItem onSelect={() => console.log("archive")}>Archive</DropdownMenuItem>
		<DropdownMenuItem onSelect={() => console.log("leave")}>Leave project</DropdownMenuItem>
		<DropdownMenuSeparator />
		<DropdownMenuItem
			variant="destructive"
			shortcut="⌫"
			onSelect={() => console.log("delete project")}
		>
			Delete project
		</DropdownMenuItem>
	</DropdownMenuContent>
</DropdownMenu>
```

### With Submenu

A nested submenu that opens on hover, click or ArrowRight.

```svelte
<script lang="ts">
	import {
		DropdownMenu,
		DropdownMenuTrigger,
		DropdownMenuContent,
		DropdownMenuItem,
		DropdownMenuSeparator,
		DropdownMenuSub,
		DropdownMenuSubTrigger,
		DropdownMenuSubContent,
	} from "fancy-ui-svelte";
</script>

<!-- `sound` on the root reaches the submenu too: DropdownMenuSub and
     DropdownMenuSubContent inherit it through context. -->
<DropdownMenu sound>
	<DropdownMenuTrigger>Actions</DropdownMenuTrigger>
	<DropdownMenuContent>
		<DropdownMenuItem onSelect={() => console.log("reload")}>Reload</DropdownMenuItem>
		<DropdownMenuSub>
			<DropdownMenuSubTrigger>More tools</DropdownMenuSubTrigger>
			<DropdownMenuSubContent>
				<DropdownMenuItem onSelect={() => console.log("screenshot")}>Screenshot</DropdownMenuItem>
				<DropdownMenuItem onSelect={() => console.log("inspect")}>Inspect</DropdownMenuItem>
			</DropdownMenuSubContent>
		</DropdownMenuSub>
		<DropdownMenuSeparator />
		<DropdownMenuItem onSelect={() => console.log("save")}>Save page…</DropdownMenuItem>
	</DropdownMenuContent>
</DropdownMenu>
```

### Disabled Items

Disabled rows are skipped by keyboard navigation and inert to click.

```svelte
<script lang="ts">
	import {
		DropdownMenu,
		DropdownMenuTrigger,
		DropdownMenuContent,
		DropdownMenuItem,
	} from "fancy-ui-svelte";
</script>

<DropdownMenu sound>
	<DropdownMenuTrigger>Edit</DropdownMenuTrigger>
	<DropdownMenuContent>
		<DropdownMenuItem onSelect={() => console.log("undo")}>Undo</DropdownMenuItem>
		<DropdownMenuItem disabled onSelect={() => console.log("redo")}>Redo</DropdownMenuItem>
		<DropdownMenuItem onSelect={() => console.log("cut")}>Cut</DropdownMenuItem>
		<DropdownMenuItem disabled onSelect={() => console.log("paste")}>Paste</DropdownMenuItem>
	</DropdownMenuContent>
</DropdownMenu>
```
