<script lang="ts">
import { Button } from "kumo-svelte/components/button";
import * as DropdownMenu from "kumo-svelte/components/dropdown";
import PlusIcon from "phosphor-svelte/lib/PlusIcon";
</script>
<DropdownMenu.Root>
<DropdownMenu.Trigger>
{#snippet child({ props })}
<Button {...props} icon={PlusIcon}>Add</Button>
{/snippet}
</DropdownMenu.Trigger>
<DropdownMenu.Content>
<DropdownMenu.Item>Worker</DropdownMenu.Item>
<DropdownMenu.Item>Pages</DropdownMenu.Item>
<DropdownMenu.Item>KV Namespace</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Root>
Import
import * as DropdownMenu from "kumo-svelte/components/dropdown"; Usage
<script lang="ts">
import { Button } from "kumo-svelte/components/button";
import * as DropdownMenu from "kumo-svelte/components/dropdown";
</script>
<DropdownMenu.Root>
<DropdownMenu.Trigger>
{#snippet child({ props })}
<Button {...props}>Menu</Button>
{/snippet}
</DropdownMenu.Trigger>
<DropdownMenu.Content>
<DropdownMenu.Item onclick={() => console.log("edit")}>Edit</DropdownMenu.Item>
<DropdownMenu.Item onclick={() => console.log("duplicate")}>Duplicate</DropdownMenu.Item>
<DropdownMenu.Separator />
<DropdownMenu.Item variant="danger" onclick={() => console.log("delete")}>Delete</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Root> Examples
Basic Dropdown
<script lang="ts">
import { Button } from "kumo-svelte/components/button";
import * as DropdownMenu from "kumo-svelte/components/dropdown";
import PlusIcon from "phosphor-svelte/lib/PlusIcon";
</script>
<DropdownMenu.Root>
<DropdownMenu.Trigger>
{#snippet child({ props })}
<Button {...props} icon={PlusIcon}>Add</Button>
{/snippet}
</DropdownMenu.Trigger>
<DropdownMenu.Content>
<DropdownMenu.Item>Worker</DropdownMenu.Item>
<DropdownMenu.Item>Pages</DropdownMenu.Item>
<DropdownMenu.Item>KV Namespace</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Root>
Inset Items
Use inset on items without an icon to align their text with items that have one.
<script lang="ts">
import { Button } from "kumo-svelte/components/button";
import * as DropdownMenu from "kumo-svelte/components/dropdown";
import CopyIcon from "phosphor-svelte/lib/CopyIcon";
import PencilSimpleIcon from "phosphor-svelte/lib/PencilSimpleIcon";
import TrashIcon from "phosphor-svelte/lib/TrashIcon";
</script>
{#snippet pencilIcon()}
<PencilSimpleIcon size={16} />
{/snippet}
{#snippet copyIcon()}
<CopyIcon size={16} />
{/snippet}
{#snippet trashIcon()}
<TrashIcon size={16} />
{/snippet}
<DropdownMenu.Root>
<DropdownMenu.Trigger>
{#snippet child({ props })}
<Button {...props}>Edit</Button>
{/snippet}
</DropdownMenu.Trigger>
<DropdownMenu.Content>
<DropdownMenu.Item icon={pencilIcon}>Rename</DropdownMenu.Item>
<DropdownMenu.Item icon={copyIcon}>Duplicate</DropdownMenu.Item>
<DropdownMenu.Separator />
<DropdownMenu.Item inset>Move to folder</DropdownMenu.Item>
<DropdownMenu.Item inset>Add to favorites</DropdownMenu.Item>
<DropdownMenu.Separator />
<DropdownMenu.Item icon={trashIcon} variant="danger">Delete</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Root>
Handling Item Clicks
Use onclick on DropdownMenu.Item to handle actions. Each item receives a standard Svelte DOM event handler.
<script lang="ts">
import { Button } from "kumo-svelte/components/button";
import * as DropdownMenu from "kumo-svelte/components/dropdown";
import CopyIcon from "phosphor-svelte/lib/CopyIcon";
import PencilSimpleIcon from "phosphor-svelte/lib/PencilSimpleIcon";
import TrashIcon from "phosphor-svelte/lib/TrashIcon";
let lastAction = $state<string | null>(null);
</script>
{#snippet copyIcon()}
<CopyIcon size={16} />
{/snippet}
{#snippet pencilIcon()}
<PencilSimpleIcon size={16} />
{/snippet}
{#snippet trashIcon()}
<TrashIcon size={16} />
{/snippet}
<div class="flex flex-col items-start gap-2">
<DropdownMenu.Root>
<DropdownMenu.Trigger>
{#snippet child({ props })}
<Button {...props}>Actions</Button>
{/snippet}
</DropdownMenu.Trigger>
<DropdownMenu.Content>
<DropdownMenu.Item icon={copyIcon} onclick={() => (lastAction = "Duplicated")}>Duplicate</DropdownMenu.Item>
<DropdownMenu.Item icon={pencilIcon} onclick={() => (lastAction = "Renamed")}>Rename</DropdownMenu.Item>
<DropdownMenu.Separator />
<DropdownMenu.Item icon={trashIcon} variant="danger" onclick={() => (lastAction = "Deleted")}>Delete</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Root>
{#if lastAction}
<p class="text-sm text-kumo-subtle">Last action: <span class="text-kumo-default">{lastAction}</span></p>
{/if}
</div>
Checkbox Items
Use DropdownMenu.CheckboxItem for toggleable options that can be independently checked or unchecked.
<script lang="ts">
import { Button } from "kumo-svelte/components/button";
import * as DropdownMenu from "kumo-svelte/components/dropdown";
let showSidebar = $state(true);
let showLineNumbers = $state(false);
let wordWrap = $state(true);
</script>
<DropdownMenu.Root>
<DropdownMenu.Trigger>
{#snippet child({ props })}
<Button {...props}>View Options</Button>
{/snippet}
</DropdownMenu.Trigger>
<DropdownMenu.Content>
<DropdownMenu.Group>
<DropdownMenu.Label>Display</DropdownMenu.Label>
<DropdownMenu.CheckboxItem bind:checked={showSidebar}>Show sidebar</DropdownMenu.CheckboxItem>
<DropdownMenu.CheckboxItem bind:checked={showLineNumbers}>Show line numbers</DropdownMenu.CheckboxItem>
<DropdownMenu.CheckboxItem bind:checked={wordWrap}>Word wrap</DropdownMenu.CheckboxItem>
</DropdownMenu.Group>
</DropdownMenu.Content>
</DropdownMenu.Root>
Nested Menu with Radio Selection
Use DropdownMenu.Sub, DropdownMenu.SubTrigger, and DropdownMenu.SubContent to create nested submenus. For single-selection lists like language or timezone, use DropdownMenu.RadioGroup and DropdownMenu.RadioItem.
<script lang="ts">
import { Button } from "kumo-svelte/components/button";
import * as DropdownMenu from "kumo-svelte/components/dropdown";
import CreditCardIcon from "phosphor-svelte/lib/CreditCardIcon";
import MoonIcon from "phosphor-svelte/lib/MoonIcon";
import SignOutIcon from "phosphor-svelte/lib/SignOutIcon";
import UserIcon from "phosphor-svelte/lib/UserIcon";
const languages = [
{ code: "de", label: "Deutsch" },
{ code: "en", label: "English" },
{ code: "es", label: "Español" },
{ code: "fr", label: "Français" },
{ code: "it", label: "Italiano" },
{ code: "pt", label: "Português" },
{ code: "ko", label: "한국어" },
{ code: "ja", label: "日本語" },
{ code: "zh-CN", label: "简体中文" },
{ code: "zh-TW", label: "繁體中文" },
];
const timezones = [
{ value: "America/Los_Angeles", label: "Pacific Time (PT)" },
{ value: "America/Denver", label: "Mountain Time (MT)" },
{ value: "America/Chicago", label: "Central Time (CT)" },
{ value: "America/New_York", label: "Eastern Time (ET)" },
{ value: "Europe/London", label: "Greenwich Mean Time (GMT)" },
{ value: "Europe/Paris", label: "Central European Time (CET)" },
{ value: "Asia/Tokyo", label: "Japan Standard Time (JST)" },
];
let language = $state("en");
let timezone = $state("America/Los_Angeles");
</script>
{#snippet userIcon()}
<UserIcon size={16} />
{/snippet}
{#snippet cardIcon()}
<CreditCardIcon size={16} />
{/snippet}
{#snippet moonIcon()}
<MoonIcon size={16} />
{/snippet}
{#snippet signOutIcon()}
<SignOutIcon size={16} />
{/snippet}
<DropdownMenu.Root>
<DropdownMenu.Trigger>
{#snippet child({ props })}
<Button {...props} icon={UserIcon}>Account</Button>
{/snippet}
</DropdownMenu.Trigger>
<DropdownMenu.Content>
<DropdownMenu.Item icon={userIcon}>Profile</DropdownMenu.Item>
<DropdownMenu.Item icon={cardIcon}>Billing</DropdownMenu.Item>
<DropdownMenu.Item icon={moonIcon}>Dark mode</DropdownMenu.Item>
<DropdownMenu.Sub>
<DropdownMenu.SubTrigger>Language</DropdownMenu.SubTrigger>
<DropdownMenu.SubContent>
<DropdownMenu.Group>
<DropdownMenu.RadioGroup bind:value={language}>
{#each languages as item}
<DropdownMenu.RadioItem value={item.code}>{item.label}</DropdownMenu.RadioItem>
{/each}
</DropdownMenu.RadioGroup>
</DropdownMenu.Group>
</DropdownMenu.SubContent>
</DropdownMenu.Sub>
<DropdownMenu.Sub>
<DropdownMenu.SubTrigger>Set Timezone</DropdownMenu.SubTrigger>
<DropdownMenu.SubContent>
<DropdownMenu.Group>
<DropdownMenu.RadioGroup bind:value={timezone}>
{#each timezones as item}
<DropdownMenu.RadioItem value={item.value}>{item.label}</DropdownMenu.RadioItem>
{/each}
</DropdownMenu.RadioGroup>
</DropdownMenu.Group>
</DropdownMenu.SubContent>
</DropdownMenu.Sub>
<DropdownMenu.Separator />
<DropdownMenu.Item icon={signOutIcon} variant="danger">Log out</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Root>
Custom Trigger with Avatar
Use the trigger child snippet to customize the trigger element while preserving accessible trigger props.
<script lang="ts">
import * as DropdownMenu from "kumo-svelte/components/dropdown";
import GearIcon from "phosphor-svelte/lib/GearIcon";
import SignOutIcon from "phosphor-svelte/lib/SignOutIcon";
import UserIcon from "phosphor-svelte/lib/UserIcon";
</script>
{#snippet userIcon()}
<UserIcon size={16} />
{/snippet}
{#snippet gearIcon()}
<GearIcon size={16} />
{/snippet}
{#snippet signOutIcon()}
<SignOutIcon size={16} />
{/snippet}
<DropdownMenu.Root>
<DropdownMenu.Trigger>
{#snippet child({ props })}
<button {...props} type="button" class="rounded-full">
<span class="flex h-8 w-8 items-center justify-center rounded-full bg-kumo-brand text-sm font-medium text-white">
MR
</span>
</button>
{/snippet}
</DropdownMenu.Trigger>
<DropdownMenu.Content>
<DropdownMenu.Item icon={userIcon}>Profile</DropdownMenu.Item>
<DropdownMenu.Item icon={gearIcon}>Settings</DropdownMenu.Item>
<DropdownMenu.Separator />
<DropdownMenu.Item icon={signOutIcon} variant="danger">Log out</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Root>
Navigation Links
Use DropdownMenu.LinkItem for menu items that navigate to a URL. This renders a semantic <a> element with full control over link attributes like target and rel.
<script lang="ts">
import { Button } from "kumo-svelte/components/button";
import * as DropdownMenu from "kumo-svelte/components/dropdown";
import ArrowSquareOutIcon from "phosphor-svelte/lib/ArrowSquareOutIcon";
import BookOpenIcon from "phosphor-svelte/lib/BookOpenIcon";
import GearIcon from "phosphor-svelte/lib/GearIcon";
</script>
{#snippet gearIcon()}
<GearIcon size={16} />
{/snippet}
{#snippet bookIcon()}
<BookOpenIcon size={16} />
{/snippet}
{#snippet externalIcon()}
<ArrowSquareOutIcon size={16} />
{/snippet}
<DropdownMenu.Root>
<DropdownMenu.Trigger>
{#snippet child({ props })}
<Button {...props}>Resources</Button>
{/snippet}
</DropdownMenu.Trigger>
<DropdownMenu.Content>
<DropdownMenu.LinkItem href="/settings" icon={gearIcon}>Settings</DropdownMenu.LinkItem>
<DropdownMenu.LinkItem href="/docs" icon={bookIcon}>Documentation</DropdownMenu.LinkItem>
<DropdownMenu.Separator />
<DropdownMenu.LinkItem href="https://developers.cloudflare.com" target="_blank" icon={externalIcon}>
Developer Docs
</DropdownMenu.LinkItem>
</DropdownMenu.Content>
</DropdownMenu.Root>
API Reference
DropdownMenu
| Prop | Type | Default |
|---|---|---|
| children | Snippet | — |
| dir | "ltr" | "rtl" | — |
| onOpenChange | (open: boolean) => void | — |
| onOpenChangeComplete | (open: boolean) => void | — |
| open | boolean | false |
DropdownMenu.CheckboxGroup
No component-specific props. This component accepts child content or standard forwarded attributes.
DropdownMenu.CheckboxItem
| Prop | Type | Default |
|---|---|---|
| children | Snippet | — |
DropdownMenu.Content
| Prop | Type | Default |
|---|---|---|
| container | PortalProps["to"] | — |
DropdownMenu.Group
No component-specific props. This component accepts child content or standard forwarded attributes.
DropdownMenu.Item
| Prop | Type | Default |
|---|---|---|
| children | Snippet | — |
| icon | Snippet | — |
| inset | boolean | false |
| selected | boolean | false |
| variant | KumoDropdownVariant | KUMO_DROPDOWN_DEFAULT_VARIANTS.variant |
DropdownMenu.Label
| Prop | Type | Default |
|---|---|---|
| inset | boolean | false |
DropdownMenu.LinkItem
| Prop | Type | Default |
|---|---|---|
| children | Snippet | — |
| icon | Snippet | — |
| inset | boolean | false |
| variant | KumoDropdownVariant | KUMO_DROPDOWN_DEFAULT_VARIANTS.variant |
DropdownMenu.Portal
| Prop | Type | Default |
|---|---|---|
| children | Snippet | — |
| to | PortalProps["to"] | — |
DropdownMenu.RadioGroup
No component-specific props. This component accepts child content or standard forwarded attributes.
DropdownMenu.RadioItem
| Prop | Type | Default |
|---|---|---|
| children | Snippet<[checked: boolean]> | — |
| icon | Snippet | — |
| inset | boolean | false |
DropdownMenu.Separator
No component-specific props. This component accepts child content or standard forwarded attributes.
DropdownMenu.Shortcut
| Prop | Type | Default |
|---|---|---|
| children | Snippet | — |
DropdownMenu.Sub
No component-specific props. This component accepts child content or standard forwarded attributes.
DropdownMenu.SubContent
No component-specific props. This component accepts child content or standard forwarded attributes.
DropdownMenu.SubTrigger
| Prop | Type | Default |
|---|---|---|
| children | Snippet | — |
| icon | Snippet | — |
| inset | boolean | false |
DropdownMenu.Trigger
No component-specific props. This component accepts child content or standard forwarded attributes.