# Dropdown Menu

Click-triggered menu popover with items, labels, separators, and nested submenus.

- Package: `@mielui/svelte`
- Component version: `2.2.1`
- Depends on Mielui components: _internal/utils, popover, button
- Shared utilities: components/_internal/button-attributes, transition, components/_internal/surface, hugeicons-icon, utils.cn, utils.createContext, utils.dynamicWidth, utils.travelingHighlight

## Install

~~~~sh
pnpm dlx @mielui/svelte add dropdown-menu
~~~~

## API

This reference is generated at build time from the component manifest, public `index.ts`, and documentation examples below. Changes to those source files are reflected here in the published Markdown. Standard Svelte and HTML attributes accepted by the exported prop types are supported.


### CheckboxItem

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| checked | boolean \| undefined | false | No | Yes | Whether the item is checked. Bindable. |
| onCheckedChange | ((checked: boolean) => void) \| undefined | - | No | No | Called with the new state when it changes. |
| children | Snippet<[]> \| undefined | - | No | No | Content rendered inside. |
| element | HTMLButtonElement \| HTMLAnchorElement \| undefined | - | No | Yes | Bindable reference to the DOM element. |
| class | (string & (ClassValue \| null)) \| undefined | - | No | No | Classes added to the element. |

Also accepts the native HTML attributes and events listed in the rendered API reference.

### Content

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| children | Snippet<[]> | - | Yes | No | Content rendered inside. |
| class | string \| undefined | - | No | No | Classes added to the element. |
| dynamic | boolean \| undefined | false | No | No | Lets the panel grow to fit its content instead of matching the trigger width. |
| tabindex | number \| undefined | -1 | No | No | Tab index of the panel. |
| role | "dialog" \| "alertdialog" \| "menu" \| "listbox" \| "none" \| undefined | 'menu' | No | No | ARIA role of the panel. |
| surface | "solid" \| "glass" \| undefined | - | No | No | Surface treatment. Omit it to inherit `--mielui-surface` from the theme. |
| allowClickOutside | boolean \| undefined | true | No | No | Closes when the pointer is pressed outside it. Set false to keep it open until it is dismissed another way. |
| dismissLayer | boolean \| undefined | true | No | No | Render the full-viewport dismiss layer under the panel while open. Defaults to `true`. Set to `false` for triggers that must stay clickable while open (e.g. an input-style combobox trigger); outside pointer dismissal still applies via `allowClickOutside`. |
| portal | boolean \| undefined | true | No | No | Moves the panel to the end of the document so ancestors cannot clip it. Set false to render it in place. |
| refElement | VirtualElement \| undefined | - | No | No | Positions the panel against this element or virtual element instead of the trigger. The panel then opens to the right of it, as a submenu does. |
| focusTrap | boolean \| undefined | false | No | No | Trap Tab focus inside the panel while open. Defaults to `true`. |
| lockScroll | boolean \| undefined | false | No | No | Lock document scrolling while the panel is open. Defaults to `true`. |

Also accepts the native HTML attributes and events listed in the rendered API reference.

### Item

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| callback | (() => void) \| undefined | - | No | No | Called when the item is chosen. |
| variant | "destructive" \| undefined | - | No | No | Marks an action that deletes or cannot be undone. Omit it for a regular item. |
| children | Snippet<[]> \| undefined | - | No | No | Content rendered inside. |
| onkeydown | ((event: KeyboardEvent) => void) \| undefined | - | No | No | Called on key down, before the button handles the key itself. |
| onclick | ((event: MouseEvent) => void) \| undefined | - | No | No | Called when the button is activated. Loading and disabled buttons do not call it. |
| status | ButtonStatus \| undefined | - | No | No | Controlled visual state. Loading remains focusable and refuses activation. |
| disabled | boolean \| undefined | false | No | No | Prevents activation. A disabled link loses its destination and is skipped by Tab. |
| element | HTMLButtonElement \| HTMLAnchorElement \| undefined | - | No | Yes | Bindable reference to the rendered DOM element. Type is the union of the two possible element types -- narrow at the use site: |
| loading | boolean \| undefined | - | No | No | Convenience alias for `status="loading"`. |
| labels | ButtonLabels \| undefined | - | No | No | Overrides the built-in text and accessible names. Every key is optional and English is the fallback. |

Also accepts the native HTML attributes and events listed in the rendered API reference.

### Label

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| children | Snippet<[]> | - | Yes | No | Content rendered inside. |
| class | string \| undefined | - | No | No | Classes added to the element. |



### RadioGroup

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| value | string \| undefined | '' | No | Yes | Value of the selected item. Bindable. |
| onValueChange | ((value: string) => void) \| undefined | - | No | No | Called with the new value when it changes. |
| children | Snippet<[]> \| undefined | - | No | No | Content rendered inside. |



### RadioItem

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| value | string \| (string & string[]) | - | Yes | No | Value this item selects. |
| children | Snippet<[]> \| undefined | - | No | No | Content rendered inside. |
| element | HTMLButtonElement \| HTMLAnchorElement \| undefined | - | No | Yes | Bindable reference to the DOM element. |
| class | (string & (ClassValue \| null)) \| undefined | - | No | No | Classes added to the element. |

Also accepts the native HTML attributes and events listed in the rendered API reference.

### Root

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| open | boolean \| undefined | false | No | Yes | Whether it is open. Bind it to control the state from outside. |
| onOpenChange | ((open: boolean) => void) \| undefined | - | No | No | Called with the new state when the person opens or closes it, not when you set `open`. |
| children | Snippet<[]> \| undefined | - | No | No | Content rendered inside. |



### Separator

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| children | Snippet<[]> \| undefined | - | No | No | Content rendered inside. |
| class | string \| undefined | - | No | No | Classes added to the element. |



### Sub

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| children | Snippet<[]> \| undefined | - | No | No | Content rendered inside. |



### SubContent

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| surface | "solid" \| "glass" \| undefined | - | No | No | Surface treatment. Omit it to inherit `--mielui-surface` from the theme. |
| children | Snippet<[]> | - | Yes | No | Content rendered inside. |
| class | string \| undefined | - | No | No | Classes added to the element. |
| dynamic | boolean \| undefined | false | No | No | Lets the panel grow to fit its content instead of matching the trigger width. |



### SubTrigger

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| class | string \| undefined | - | No | No | Additional classes for the element. |
| children | Snippet<[]> \| undefined | - | No | No | Content rendered inside. |
| element | HTMLButtonElement \| HTMLAnchorElement \| undefined | - | No | Yes | Bindable reference to the DOM element. |
| onclick | ((event: MouseEvent) => void) \| undefined | - | No | No | Called when it is clicked or activated. |
| onopen | (() => void) \| undefined | - | No | No | Called immediately before this trigger opens its submenu. |
| style | string \| undefined | - | No | No | Inline styles for the element. |

Also accepts the native HTML attributes and events listed in the rendered API reference.

### Trigger

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| children | Snippet<[]> | - | Yes | No | Content rendered inside. |
| class | string \| undefined | - | No | No | Classes added to the element. |
| variant | ButtonVariant \| undefined | - | No | No | Button style of the trigger. |
| style | string \| undefined | - | No | No | Inline styles for the element. |
| onclick | ((event: MouseEvent) => void) \| undefined | - | No | No | Called when it is clicked or activated. |
| size | "sm" \| "md" \| "lg" \| "icon" \| undefined | - | No | No | Control height of the trigger. |
| element | HTMLButtonElement \| HTMLAnchorElement \| undefined | - | No | Yes | Bindable reference to the DOM element. |
| unstyled | boolean \| undefined | - | No | No | Forwarded to Button: render with `class` alone, skipping variant/size. |
| icon | boolean \| undefined | false | No | No | Shows the chevron after the label. |
| onopen | (() => void) \| undefined | - | No | No | Called immediately before this trigger opens its popover. |

Also accepts the native HTML attributes and events listed in the rendered API reference.

### Public types

~~~~ts
import type { DefaultProps } from '@mielui/svelte/utils';
import type { Snippet } from 'svelte';
import type { HTMLButtonAttributes } from 'svelte/elements';
import type { MenuRowButtonProps } from '../_internal/button-attributes';
import Root from './dropdown-menu.svelte';
import CheckboxItem from './dropdown-menu-checkbox-item.svelte';
import Content from './dropdown-menu-content.svelte';
import Item from './dropdown-menu-item.svelte';
import Label from './dropdown-menu-label.svelte';
import RadioGroup from './dropdown-menu-radio-group.svelte';
import RadioItem from './dropdown-menu-radio-item.svelte';
import Separator from './dropdown-menu-separator.svelte';
import Sub from './dropdown-menu-sub.svelte';
import SubContent from './dropdown-menu-sub-content.svelte';
import SubTrigger from './dropdown-menu-sub-trigger.svelte';
import Trigger from './dropdown-menu-trigger.svelte';

export type DropdownMenuProps = {
    /** Whether it is open. Bind it to control the state from outside. */
    open?: boolean;
    /** Called with the new state when the person opens or closes it, not when you set `open`. */
    onOpenChange?: (open: boolean) => void;
    /** Content rendered inside. */
    children?: Snippet;
};

export type DropdownMenuItemProps = {
    /** Called when the item is chosen. */
    callback?: () => void;
    /** Marks an action that deletes or cannot be undone. Omit it for a regular item. */
    variant?: 'destructive';
} & MenuRowButtonProps;

export type DropdownMenuRadioGroupProps = {
    /** Value of the selected item. Bindable. */
    value?: string;
    /** Called with the new value when it changes. */
    onValueChange?: (value: string) => void;
    /** Content rendered inside. */
    children?: Snippet;
};

export type DropdownMenuRadioItemProps = {
    /** Value this item selects. */
    value: string;
    /** Content rendered inside. */
    children?: Snippet;
    /** Bindable reference to the DOM element. */
    element?: HTMLButtonElement | HTMLAnchorElement;
} & DefaultProps &
    Omit<HTMLButtonAttributes, 'children' | 'role' | 'aria-checked'>;

export type DropdownMenuCheckboxItemProps = {
    /** Whether the item is checked. Bindable. */
    checked?: boolean;
    /** Called with the new state when it changes. */
    onCheckedChange?: (checked: boolean) => void;
    /** Content rendered inside. */
    children?: Snippet;
    /** Bindable reference to the DOM element. */
    element?: HTMLButtonElement | HTMLAnchorElement;
} & DefaultProps &
    Omit<HTMLButtonAttributes, 'children' | 'role' | 'aria-checked'>;

export {
    CheckboxItem,
    Content,
    Item,
    Label,
    RadioGroup,
    RadioItem,
    Root,
    Separator,
    Sub,
    SubContent,
    SubTrigger,
    Trigger
};
~~~~

## Examples

### Basic Menu

~~~~svelte
<script lang="ts">
    import * as DropdownMenu from '@mielui/svelte/components/dropdown-menu';

    let order = $state('Newest first');

    function sort(value: string) {
        order = value;
    }
</script>

<div class="flex flex-col items-center gap-3">
    <DropdownMenu.Root>
        <DropdownMenu.Trigger>Sort files</DropdownMenu.Trigger>
        <DropdownMenu.Content>
            <DropdownMenu.Item callback={() => sort('Newest first')}>
                Newest first
            </DropdownMenu.Item>
            <DropdownMenu.Item callback={() => sort('Name A to Z')}>Name A to Z</DropdownMenu.Item>
        </DropdownMenu.Content>
    </DropdownMenu.Root>
    <p role="status" class="text-sm text-foreground-muted">{order}</p>
</div>
~~~~

### Checkbox Radio

~~~~svelte
<script lang="ts">
    import * as DropdownMenu from '@mielui/svelte/components/dropdown-menu';

    let wrap = $state(true);
    let minimap = $state(false);
    let density = $state('comfortable');
    let lastChange = $state('No changes yet');
</script>

<div class="flex flex-col items-center gap-3">
    <DropdownMenu.Root>
        <DropdownMenu.Trigger variant="outline">View options</DropdownMenu.Trigger>
        <DropdownMenu.Content class="min-w-[13rem]">
            <DropdownMenu.Label>Editor</DropdownMenu.Label>
            <DropdownMenu.CheckboxItem
                bind:checked={wrap}
                onCheckedChange={(checked) => {
                    lastChange = `Word wrap ${checked ? 'on' : 'off'}`;
                }}
            >
                Word wrap
            </DropdownMenu.CheckboxItem>
            <DropdownMenu.CheckboxItem bind:checked={minimap}>Minimap</DropdownMenu.CheckboxItem>
            <DropdownMenu.Separator />
            <DropdownMenu.Label>Density</DropdownMenu.Label>
            <DropdownMenu.RadioGroup
                bind:value={density}
                onValueChange={(value) => {
                    lastChange = `Density set to ${value}`;
                }}
            >
                <DropdownMenu.RadioItem value="compact">Compact</DropdownMenu.RadioItem>
                <DropdownMenu.RadioItem value="comfortable">Comfortable</DropdownMenu.RadioItem>
                <DropdownMenu.RadioItem value="spacious">Spacious</DropdownMenu.RadioItem>
            </DropdownMenu.RadioGroup>
        </DropdownMenu.Content>
    </DropdownMenu.Root>
    <p class="text-sm text-foreground-muted">{lastChange}</p>
</div>
~~~~

### Configuration

~~~~svelte
<script lang="ts">
    import {
        Tick02Icon as Check,
        ArrowDown01Icon as ChevronDown
    } from '@hugeicons/core-free-icons';
    import * as DropdownMenu from '@mielui/svelte/components/dropdown-menu';
    import HugeiconsIcon from '@mielui/svelte/hugeicons-icon';

    let model = $state('GPT-5.6 Sol');
    let effort = $state('High');
    let speed = $state('Standard');
</script>

<DropdownMenu.Root>
    <DropdownMenu.Trigger variant="outline" class="min-w-[11rem] justify-between">
        <span>Configuration</span>
        <HugeiconsIcon icon={ChevronDown} size={15} class="text-foreground-muted" />
    </DropdownMenu.Trigger>
    <DropdownMenu.Content class="min-w-[11rem]">
        <DropdownMenu.Label>Configuration</DropdownMenu.Label>
        <DropdownMenu.Sub>
            <DropdownMenu.SubTrigger>Model</DropdownMenu.SubTrigger>
            <DropdownMenu.SubContent class="min-w-[12rem]">
                <DropdownMenu.Item callback={() => (model = 'GPT-5.6 Sol')}>
                    <span>GPT-5.6 Sol</span>
                    {#if model === 'GPT-5.6 Sol'}
                        <HugeiconsIcon icon={Check} size={16} class="ml-auto" />
                    {/if}
                </DropdownMenu.Item>
                <DropdownMenu.Item callback={() => (model = 'Opus 4.6')}>
                    <span>Opus 4.6</span>
                    {#if model === 'Opus 4.6'}
                        <HugeiconsIcon icon={Check} size={16} class="ml-auto" />
                    {/if}
                </DropdownMenu.Item>
            </DropdownMenu.SubContent>
        </DropdownMenu.Sub>
        <DropdownMenu.Sub>
            <DropdownMenu.SubTrigger>Effort</DropdownMenu.SubTrigger>
            <DropdownMenu.SubContent class="min-w-[10rem]">
                {#each ['Low', 'Medium', 'High'] as option (option)}
                    <DropdownMenu.Item callback={() => (effort = option)}>
                        <span>{option}</span>
                        {#if effort === option}
                            <HugeiconsIcon icon={Check} size={16} class="ml-auto" />
                        {/if}
                    </DropdownMenu.Item>
                {/each}
            </DropdownMenu.SubContent>
        </DropdownMenu.Sub>
        <DropdownMenu.Sub>
            <DropdownMenu.SubTrigger>Speed</DropdownMenu.SubTrigger>
            <DropdownMenu.SubContent class="min-w-[10rem]">
                {#each ['Fast', 'Standard', 'Deliberate'] as option (option)}
                    <DropdownMenu.Item callback={() => (speed = option)}>
                        <span>{option}</span>
                        {#if speed === option}
                            <HugeiconsIcon icon={Check} size={16} class="ml-auto" />
                        {/if}
                    </DropdownMenu.Item>
                {/each}
            </DropdownMenu.SubContent>
        </DropdownMenu.Sub>
    </DropdownMenu.Content>
</DropdownMenu.Root>
~~~~

### Controlled

~~~~svelte
<script lang="ts">
    import { Button } from '@mielui/svelte/components/button';
    import * as DropdownMenu from '@mielui/svelte/components/dropdown-menu';

    const branches = ['main', 'feat/rate-limit', 'fix/invoice-test-clock'];

    let open = $state(false);
    let loads = $state(0);
    let lastChange = $state('none yet');
</script>

<div class="flex flex-col items-center gap-3">
    <div class="flex items-center gap-2">
        <DropdownMenu.Root
            bind:open
            onOpenChange={(next) => {
                lastChange = next ? 'opened' : 'closed';
            }}
        >
            <DropdownMenu.Trigger
                variant="outline"
                onopen={() => {
                    loads += 1;
                }}
            >
                Switch branch
            </DropdownMenu.Trigger>
            <DropdownMenu.Content class="min-w-[13rem]">
                {#each branches as branch (branch)}
                    <DropdownMenu.Item>{branch}</DropdownMenu.Item>
                {/each}
            </DropdownMenu.Content>
        </DropdownMenu.Root>
        <Button
            variant="ghost"
            onclick={() => {
                open = true;
            }}
        >
            Open it from here
        </Button>
    </div>
    <p class="text-sm text-foreground-muted">
        Trigger opened it {loads} times. Last change by a person: {lastChange}.
    </p>
</div>
~~~~

### Dynamic Width

~~~~svelte
<script lang="ts">
    import { Tick02Icon as Check } from '@hugeicons/core-free-icons';
    import * as DropdownMenu from '@mielui/svelte/components/dropdown-menu';
    import HugeiconsIcon from '@mielui/svelte/hugeicons-icon';

    const densities = ['Compact', 'Comfortable', 'Spacious'];
    let density = $state(densities[1]);
</script>

<DropdownMenu.Root>
    <DropdownMenu.Trigger>Open menu</DropdownMenu.Trigger>
    <DropdownMenu.Content dynamic>
        <DropdownMenu.Label>Appearance</DropdownMenu.Label>
        <DropdownMenu.Item>Edit dashboard settings</DropdownMenu.Item>
        <DropdownMenu.Sub>
            <DropdownMenu.SubTrigger>Density</DropdownMenu.SubTrigger>
            <DropdownMenu.SubContent dynamic>
                {#each densities as option (option)}
                    <DropdownMenu.Item callback={() => (density = option)}>
                        <span class="flex-1">{option}</span>
                        {#if density === option}
                            <HugeiconsIcon icon={Check} size={13} aria-hidden="true" />
                        {/if}
                    </DropdownMenu.Item>
                {/each}
            </DropdownMenu.SubContent>
        </DropdownMenu.Sub>
        <DropdownMenu.Item>Reset everything to defaults</DropdownMenu.Item>
    </DropdownMenu.Content>
</DropdownMenu.Root>
~~~~

### Glass

~~~~svelte
<script lang="ts">
    import {
        ArrowDown01Icon as ChevronDown,
        CreditCardIcon as CreditCard,
        LifebuoyIcon as LifeBuoy,
        Logout01Icon as LogOut,
        Settings01Icon as Settings,
        UserIcon as User
    } from '@hugeicons/core-free-icons';
    import * as DropdownMenu from '@mielui/svelte/components/dropdown-menu';
    import HugeiconsIcon from '@mielui/svelte/hugeicons-icon';

    let section = $state('Profile');
    const descriptions: Record<string, string> = {
        Profile: 'Alex Morgan · alex@example.com',
        Billing: 'Team plan · Next invoice October 1',
        Settings: 'Email notifications enabled',
        'Help & feedback': 'Contact your workspace owner for help.',
        'Sign out': 'Signed out of this preview.'
    };

    function selectSection(value: string) {
        section = value;
    }
</script>

<div class="flex flex-col items-center gap-3">
    <DropdownMenu.Root>
        <DropdownMenu.Trigger variant="outline">
            My account
            <HugeiconsIcon icon={ChevronDown} size={16} class="text-foreground-muted" />
        </DropdownMenu.Trigger>
        <DropdownMenu.Content surface="glass" class="min-w-[16rem]">
            <DropdownMenu.Label>
                <span class="text-xs text-foreground-muted">alex@example.com</span>
            </DropdownMenu.Label>
            <DropdownMenu.Item callback={() => selectSection('Profile')}>
                <span class="flex items-center gap-2">
                    <HugeiconsIcon icon={User} size={13} />
                    Profile
                </span>
            </DropdownMenu.Item>
            <DropdownMenu.Item callback={() => selectSection('Billing')}>
                <span class="flex items-center gap-2">
                    <HugeiconsIcon icon={CreditCard} size={13} />
                    Billing
                </span>
            </DropdownMenu.Item>
            <DropdownMenu.Item callback={() => selectSection('Settings')}>
                <span class="flex items-center gap-2">
                    <HugeiconsIcon icon={Settings} size={13} />
                    Settings
                </span>
            </DropdownMenu.Item>
            <DropdownMenu.Separator />
            <DropdownMenu.Item callback={() => selectSection('Help & feedback')}>
                <span class="flex items-center gap-2">
                    <HugeiconsIcon icon={LifeBuoy} size={13} />
                    Help & feedback
                </span>
            </DropdownMenu.Item>
            <DropdownMenu.Item variant="destructive" callback={() => selectSection('Sign out')}>
                <span class="flex items-center gap-2">
                    <HugeiconsIcon icon={LogOut} size={13} />
                    Sign out
                </span>
            </DropdownMenu.Item>
        </DropdownMenu.Content>
    </DropdownMenu.Root>

    <p role="status" class="text-sm text-foreground-muted">{descriptions[section]}</p>
</div>
~~~~

### Hero

~~~~svelte
<script lang="ts">
    import {
        ArrowDown01Icon as ChevronDown,
        CreditCardIcon as CreditCard,
        LifebuoyIcon as LifeBuoy,
        Logout01Icon as LogOut,
        Settings01Icon as Settings,
        UserIcon as User
    } from '@hugeicons/core-free-icons';
    import * as DropdownMenu from '@mielui/svelte/components/dropdown-menu';
    import HugeiconsIcon from '@mielui/svelte/hugeicons-icon';

    let section = $state('Profile');
    const descriptions: Record<string, string> = {
        Profile: 'Alex Morgan · alex@example.com',
        Billing: 'Team plan · Next invoice October 1',
        Settings: 'Email notifications enabled',
        'Help & feedback': 'Contact your workspace owner for help.',
        'Sign out': 'Signed out of this preview.'
    };

    function selectSection(value: string) {
        section = value;
    }
</script>

<div class="flex flex-col items-center gap-3">
    <DropdownMenu.Root>
        <DropdownMenu.Trigger variant="outline">
            My account
            <HugeiconsIcon icon={ChevronDown} size={16} class="text-foreground-muted" />
        </DropdownMenu.Trigger>
        <DropdownMenu.Content class="min-w-[16rem]">
            <DropdownMenu.Label>
                <span class="text-xs text-foreground-muted">alex@example.com</span>
            </DropdownMenu.Label>
            <DropdownMenu.Item callback={() => selectSection('Profile')}>
                <span class="flex items-center gap-2">
                    <HugeiconsIcon icon={User} size={13} />
                    Profile
                </span>
            </DropdownMenu.Item>
            <DropdownMenu.Item callback={() => selectSection('Billing')}>
                <span class="flex items-center gap-2">
                    <HugeiconsIcon icon={CreditCard} size={13} />
                    Billing
                </span>
            </DropdownMenu.Item>
            <DropdownMenu.Item callback={() => selectSection('Settings')}>
                <span class="flex items-center gap-2">
                    <HugeiconsIcon icon={Settings} size={13} />
                    Settings
                </span>
            </DropdownMenu.Item>
            <DropdownMenu.Separator />
            <DropdownMenu.Item callback={() => selectSection('Help & feedback')}>
                <span class="flex items-center gap-2">
                    <HugeiconsIcon icon={LifeBuoy} size={13} />
                    Help & feedback
                </span>
            </DropdownMenu.Item>
            <DropdownMenu.Item variant="destructive" callback={() => selectSection('Sign out')}>
                <span class="flex items-center gap-2">
                    <HugeiconsIcon icon={LogOut} size={13} />
                    Sign out
                </span>
            </DropdownMenu.Item>
        </DropdownMenu.Content>
    </DropdownMenu.Root>

    <p role="status" class="text-sm text-foreground-muted">{descriptions[section]}</p>
</div>
~~~~

### Row Actions

~~~~svelte
<script lang="ts">
    import {
        ArchiveIcon as Archive,
        Files01Icon as Copy2,
        MoreHorizontalIcon as MoreHorizontal,
        PencilEdit01Icon as Pencil,
        Delete02Icon as Trash
    } from '@hugeicons/core-free-icons';
    import * as DropdownMenu from '@mielui/svelte/components/dropdown-menu';
    import HugeiconsIcon from '@mielui/svelte/hugeicons-icon';

    let fileName = $state('Release notes.md');
    let copies = $state(1);
    let message = $state('');

    function rename() {
        fileName = 'Release notes, final.md';
    }

    function duplicate() {
        copies += 1;
        message = `Created copy ${copies}.`;
    }

    function archive() {
        message = 'File moved to the local archive.';
    }

    function remove() {
        message = 'File removed from this preview.';
    }
</script>

<div class="flex flex-col items-center gap-3">
    <p class="text-sm">{fileName}</p>
    <DropdownMenu.Root>
        <DropdownMenu.Trigger variant="ghost" size="icon" aria-label="Row actions">
            <HugeiconsIcon icon={MoreHorizontal} size={14} />
        </DropdownMenu.Trigger>
        <DropdownMenu.Content class="min-w-[12rem]">
            <DropdownMenu.Item callback={rename}>
                <span class="flex items-center gap-2">
                    <HugeiconsIcon icon={Pencil} size={13} />
                    Rename
                </span>
            </DropdownMenu.Item>
            <DropdownMenu.Item callback={duplicate}>
                <span class="flex items-center gap-2">
                    <HugeiconsIcon icon={Copy2} size={13} />
                    Duplicate
                </span>
            </DropdownMenu.Item>
            <DropdownMenu.Item callback={archive}>
                <span class="flex items-center gap-2">
                    <HugeiconsIcon icon={Archive} size={13} />
                    Archive
                </span>
            </DropdownMenu.Item>
            <DropdownMenu.Separator />
            <DropdownMenu.Item variant="destructive" callback={remove}>
                <span class="flex items-center gap-2">
                    <HugeiconsIcon icon={Trash} size={13} />
                    Delete
                </span>
            </DropdownMenu.Item>
        </DropdownMenu.Content>
    </DropdownMenu.Root>

    <p role="status" class="text-sm text-foreground-muted">{message}</p>
</div>
~~~~

### Share Menu

~~~~svelte
<script lang="ts">
    import {
        ArrowDown01Icon as ChevronDown,
        Files01Icon as Copy2,
        ViewOffIcon as EyeOff,
        SentIcon as Send,
        UserIcon as User
    } from '@hugeicons/core-free-icons';
    import * as DropdownMenu from '@mielui/svelte/components/dropdown-menu';
    import HugeiconsIcon from '@mielui/svelte/hugeicons-icon';

    let privateProject = $state(false);
    let message = $state('');

    function invite() {
        message = 'Share the copied link with a collaborator.';
    }

    async function copyLink() {
        try {
            await navigator.clipboard.writeText(window.location.href);
            message = 'Documentation link copied.';
        } catch {
            message = 'Clipboard access is unavailable.';
        }
    }

    function toggleVisibility() {
        privateProject = !privateProject;
        message = privateProject ? 'Preview project is private.' : 'Preview project is public.';
    }
</script>

<div class="flex flex-col items-center gap-3">
    <DropdownMenu.Root>
        <DropdownMenu.Trigger variant="outline">
            <HugeiconsIcon icon={Send} size={13} />
            Share
            <HugeiconsIcon icon={ChevronDown} size={11} class="text-foreground-muted" />
        </DropdownMenu.Trigger>
        <DropdownMenu.Content class="min-w-[14rem]">
            <DropdownMenu.Label>Workspace</DropdownMenu.Label>
            <DropdownMenu.Item callback={invite}>
                <span class="flex items-center gap-2">
                    <HugeiconsIcon icon={User} size={13} />
                    Invite collaborator
                </span>
            </DropdownMenu.Item>
            <DropdownMenu.Item callback={copyLink}>
                <span class="flex items-center gap-2">
                    <HugeiconsIcon icon={Copy2} size={13} />
                    Copy link
                </span>
            </DropdownMenu.Item>
            <DropdownMenu.Separator />
            <DropdownMenu.Label>Public</DropdownMenu.Label>
            <DropdownMenu.Item callback={toggleVisibility}>
                <span class="flex items-center gap-2">
                    <HugeiconsIcon icon={EyeOff} size={13} />
                    {privateProject ? 'Make public' : 'Make private'}
                </span>
            </DropdownMenu.Item>
        </DropdownMenu.Content>
    </DropdownMenu.Root>

    <p role="status" class="text-sm text-foreground-muted">{message}</p>
</div>
~~~~

### Sort Menu

~~~~svelte
<script lang="ts">
    import {
        Tick02Icon as Check,
        ArrowDown01Icon as ChevronDown,
        StarIcon as Star
    } from '@hugeicons/core-free-icons';
    import * as DropdownMenu from '@mielui/svelte/components/dropdown-menu';
    import HugeiconsIcon from '@mielui/svelte/hugeicons-icon';

    const options = ['Most starred', 'Recently updated', 'Alphabetical', 'Oldest first'];
    let selected = $state(options[0]);
</script>

<DropdownMenu.Root>
    <DropdownMenu.Trigger variant="ghost">
        <HugeiconsIcon icon={Star} size={13} />
        {selected}
        <HugeiconsIcon icon={ChevronDown} size={11} class="text-foreground-muted" />
    </DropdownMenu.Trigger>
    <DropdownMenu.Content class="min-w-[12rem]">
        {#each options as option (option)}
            <DropdownMenu.Item callback={() => (selected = option)}>
                <span>{option}</span>
                {#if selected === option}
                    <HugeiconsIcon icon={Check} size={12} class="ml-auto text-primary" />
                {/if}
            </DropdownMenu.Item>
        {/each}
    </DropdownMenu.Content>
</DropdownMenu.Root>
~~~~

### User Menu

~~~~svelte
<script lang="ts">
    import {
        ArrowDown01Icon as ChevronDown,
        CreditCardIcon as CreditCard,
        FolderGitTwoIcon as Folder,
        HelpCircleIcon as HelpCircle,
        Logout01Icon as LogOut,
        Settings01Icon as Settings,
        UserIcon as User
    } from '@hugeicons/core-free-icons';
    import * as DropdownMenu from '@mielui/svelte/components/dropdown-menu';
    import HugeiconsIcon from '@mielui/svelte/hugeicons-icon';
</script>

<DropdownMenu.Root>
    <DropdownMenu.Trigger variant="outline">
        <HugeiconsIcon icon={User} size={13} />
        mielsense
        <HugeiconsIcon icon={ChevronDown} size={11} class="text-foreground-muted" />
    </DropdownMenu.Trigger>
    <DropdownMenu.Content class="min-w-[14rem]">
        <DropdownMenu.Label>Account</DropdownMenu.Label>
        <DropdownMenu.Item>
            <span class="flex items-center gap-2">
                <HugeiconsIcon icon={User} size={13} />
                Profile
            </span>
        </DropdownMenu.Item>
        <DropdownMenu.Item>
            <span class="flex items-center gap-2">
                <HugeiconsIcon icon={Folder} size={13} />
                Your projects
            </span>
        </DropdownMenu.Item>
        <DropdownMenu.Item>
            <span class="flex items-center gap-2">
                <HugeiconsIcon icon={CreditCard} size={13} />
                Billing
            </span>
        </DropdownMenu.Item>

        <DropdownMenu.Separator />

        <DropdownMenu.Label>Settings</DropdownMenu.Label>
        <DropdownMenu.Item>
            <span class="flex items-center gap-2">
                <HugeiconsIcon icon={Settings} size={13} />
                Preferences
            </span>
        </DropdownMenu.Item>
        <DropdownMenu.Item>
            <span class="flex items-center gap-2">
                <HugeiconsIcon icon={HelpCircle} size={13} />
                Help &amp; support
            </span>
        </DropdownMenu.Item>
        <DropdownMenu.Item>
            <span class="flex items-center gap-2">
                <HugeiconsIcon icon={LogOut} size={13} />
                Log out
            </span>
        </DropdownMenu.Item>
    </DropdownMenu.Content>
</DropdownMenu.Root>
~~~~

For the rendered reference, visit [/docs/components/dropdown-menu](/docs/components/dropdown-menu).
