Dropdown Menu
kumo-svelte
<script lang="ts">
  import { Plus } from 'phosphor-svelte';
  import { Button, DropdownMenu } from 'kumo-svelte';
</script>

<DropdownMenu>
  <DropdownMenu.Trigger>
    <Button icon={Plus}>Add</Button>
  </DropdownMenu.Trigger>
  <DropdownMenu.Content>
    <DropdownMenu.Item>Worker</DropdownMenu.Item>
    <DropdownMenu.Item>Pages</DropdownMenu.Item>
    <DropdownMenu.Item>KV Namespace</DropdownMenu.Item>
  </DropdownMenu.Content>
</DropdownMenu>

Installation

Barrel

import { DropdownMenu } from 'kumo-svelte';

Granular

import { DropdownMenu } from 'kumo-svelte/components/dropdown-menu';

Usage

<script>  import { DropdownMenu, Button } from "kumo-svelte";</script><DropdownMenu>  <DropdownMenu.Trigger>    {#snippet child({ props })}      <Button {...props}>Menu</Button>    {/snippet}  </DropdownMenu.Trigger>  <DropdownMenu.Content>    <DropdownMenu.Item onSelect={() => console.log("edit")}>      Edit    </DropdownMenu.Item>    <DropdownMenu.Item onSelect={() => console.log("duplicate")}>      Duplicate    </DropdownMenu.Item>    <DropdownMenu.Separator />    <DropdownMenu.Item      variant="danger"      onSelect={() => console.log("delete")}    >      Delete    </DropdownMenu.Item>  </DropdownMenu.Content></DropdownMenu>

Examples

Basic Dropdown

<script lang="ts">
  import { Plus } from 'phosphor-svelte';
  import { Button, DropdownMenu } from 'kumo-svelte';
</script>

<DropdownMenu>
  <DropdownMenu.Trigger>
    <Button icon={Plus}>Add</Button>
  </DropdownMenu.Trigger>
  <DropdownMenu.Content>
    <DropdownMenu.Item>Worker</DropdownMenu.Item>
    <DropdownMenu.Item>Pages</DropdownMenu.Item>
    <DropdownMenu.Item>KV Namespace</DropdownMenu.Item>
  </DropdownMenu.Content>
</DropdownMenu>

Inset Items

Use inset on items without an icon to align their text with items that have one.

<script lang="ts">
  import { Copy, PencilSimple, Trash } from 'phosphor-svelte';
  import { Button, DropdownMenu } from 'kumo-svelte';
</script>

<DropdownMenu>
  <DropdownMenu.Trigger>
    <Button>Edit</Button>
  </DropdownMenu.Trigger>
  <DropdownMenu.Content>
    <DropdownMenu.Item icon={PencilSimple}>Rename</DropdownMenu.Item>
    <DropdownMenu.Item icon={Copy}>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={Trash} variant="danger">Delete</DropdownMenu.Item>
  </DropdownMenu.Content>
</DropdownMenu>

Handling Item Clicks

Use onSelect on DropdownMenu.Item to handle actions. Each item receives a standard menu select event handler.

<script lang="ts">
  import { Copy, PencilSimple, Trash } from 'phosphor-svelte';
  import { Button, DropdownMenu } from 'kumo-svelte';

  let lastAction = $state<string | null>(null);
</script>

<div class="flex flex-col items-start gap-2">
  <DropdownMenu>
    <DropdownMenu.Trigger>
      <Button>Actions</Button>
    </DropdownMenu.Trigger>
    <DropdownMenu.Content>
      <DropdownMenu.Item icon={Copy} onSelect={() => (lastAction = 'Duplicated')}>
        Duplicate
      </DropdownMenu.Item>
      <DropdownMenu.Item icon={PencilSimple} onSelect={() => (lastAction = 'Renamed')}>
        Rename
      </DropdownMenu.Item>
      <DropdownMenu.Separator />
      <DropdownMenu.Item icon={Trash} variant="danger" onSelect={() => (lastAction = 'Deleted')}>
        Delete
      </DropdownMenu.Item>
    </DropdownMenu.Content>
  </DropdownMenu>
  {#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, DropdownMenu } from 'kumo-svelte';

  let showSidebar = $state(true);
  let showLineNumbers = $state(false);
  let wordWrap = $state(true);
</script>

<DropdownMenu>
  <DropdownMenu.Trigger>
    <Button>View Options</Button>
  </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>

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 { CreditCard, Moon, SignOut, User } from 'phosphor-svelte';
  import { Button, DropdownMenu } from 'kumo-svelte';

  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>

<DropdownMenu>
  <DropdownMenu.Trigger>
    <Button icon={User}>Account</Button>
  </DropdownMenu.Trigger>
  <DropdownMenu.Content>
    <DropdownMenu.Item icon={User}>Profile</DropdownMenu.Item>
    <DropdownMenu.Item icon={CreditCard}>Billing</DropdownMenu.Item>
    <DropdownMenu.Item icon={Moon}>Dark mode</DropdownMenu.Item>

    <DropdownMenu.Sub>
      <DropdownMenu.SubTrigger>Language</DropdownMenu.SubTrigger>
      <DropdownMenu.SubContent>
        <DropdownMenu.Group>
          <DropdownMenu.RadioGroup bind:value={language}>
            {#each languages as lang}
              <DropdownMenu.RadioItem value={lang.code}>
                {lang.label}
                <DropdownMenu.RadioItemIndicator />
              </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 tz}
              <DropdownMenu.RadioItem value={tz.value}>
                {tz.label}
                <DropdownMenu.RadioItemIndicator />
              </DropdownMenu.RadioItem>
            {/each}
          </DropdownMenu.RadioGroup>
        </DropdownMenu.Group>
      </DropdownMenu.SubContent>
    </DropdownMenu.Sub>

    <DropdownMenu.Separator />
    <DropdownMenu.Item icon={SignOut} variant="danger">Log out</DropdownMenu.Item>
  </DropdownMenu.Content>
</DropdownMenu>

Custom Trigger with Avatar

Use the child snippet to customize the trigger element while passing children to render inside it. This is useful when you need a non-button trigger (like an avatar) wrapped in an accessible button element.

<script lang="ts">
  import { Gear, SignOut, User } from 'phosphor-svelte';
  import { DropdownMenu } from 'kumo-svelte';
</script>

<DropdownMenu>
  <DropdownMenu.Trigger>
    <button 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>
  </DropdownMenu.Trigger>
  <DropdownMenu.Content>
    <DropdownMenu.Item icon={User}>Profile</DropdownMenu.Item>
    <DropdownMenu.Item icon={Gear}>Settings</DropdownMenu.Item>
    <DropdownMenu.Separator />
    <DropdownMenu.Item icon={SignOut} variant="danger">Log out</DropdownMenu.Item>
  </DropdownMenu.Content>
</DropdownMenu>

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 { ArrowSquareOut, BookOpen, Gear } from 'phosphor-svelte';
  import { Button, DropdownMenu } from 'kumo-svelte';
</script>

<DropdownMenu>
  <DropdownMenu.Trigger>
    <Button>Resources</Button>
  </DropdownMenu.Trigger>
  <DropdownMenu.Content>
    <DropdownMenu.LinkItem href="/settings" icon={Gear}>
      Settings
    </DropdownMenu.LinkItem>
    <DropdownMenu.LinkItem href="/docs" icon={BookOpen}>
      Documentation
    </DropdownMenu.LinkItem>
    <DropdownMenu.Separator />
    <DropdownMenu.LinkItem
      href="https://developers.cloudflare.com"
      target="_blank"
      icon={ArrowSquareOut}
    >
      Developer Docs
    </DropdownMenu.LinkItem>
  </DropdownMenu.Content>
</DropdownMenu>

API Reference

Root component that manages the dropdown state.

PropTypeDefaultDescription
children Snippet-Child snippet rendered by the component.
open booleanfalseControlled open state.
onOpenChange (open: boolean) => void-Called when open state changes.

Button that opens the dropdown when clicked.

PropTypeDefaultDescription
child Snippet<[{ props: Record<string, unknown> }]>-child prop.
type 'button' | 'submit' | 'reset'"button"Element or semantic type.

Individual menu item for actions.

PropTypeDefaultDescription
href string-Deprecated destination URL for legacy link items.
icon Component-Icon rendered by the component.
variant 'default' | 'danger'"default"Visual variant.
selected booleanfalseShows a trailing selected check indicator.
inset booleanfalseIndents the item content.
disabled boolean-Disables the component.

A menu item that navigates to a URL. Renders a semantic <a> element. Use this instead of Item for navigation links.

PropTypeDefaultDescription
href string-Deprecated destination URL for legacy link items.
icon Component-Icon rendered by the component.
variant 'default' | 'danger'"default"Visual variant.
selected booleanfalseShows a trailing selected check indicator.
inset booleanfalseIndents the item content.
disabled boolean-Disables the component.

A menu item that can be toggled on or off. Use for independent boolean options.

PropTypeDefaultDescription
checked booleanfalseChecked state.
onCheckedChange (checked: boolean) => void-Called when checked changes.

Root component for a nested submenu. Wrap SubTrigger and SubContent inside this.

PropTypeDefaultDescription
open booleanfalseControlled open state.
onOpenChange (open: boolean) => void-Called when open state changes.

Menu item that opens a nested submenu when hovered or clicked. Displays a caret icon automatically.

PropTypeDefaultDescription
icon Component-Icon rendered by the component.
inset booleanfalseIndents the item content.

Container for submenu items. Positioned relative to the SubTrigger.

PropTypeDefaultDescription
sideOffset number8Distance from the anchor.
side 'top' | 'right' | 'bottom' | 'left'-Preferred floating side.
align 'start' | 'center' | 'end'-Floating alignment.
container HTMLElement | stringdocument.bodyPortal container for custom roots or Shadow DOM.

A visual divider between menu items.

PropTypeDefaultDescription
No component-specific props. Accepts standard HTML attributes.

Groups radio items for single-selection behavior. Only one item can be selected at a time.

PropTypeDefaultDescription
No component-specific props. Accepts standard HTML attributes.

A menu item that works like a radio button. Must be used inside a RadioGroup.

PropTypeDefaultDescription
value *string-Controlled value.
icon Component-Icon rendered by the component.
inset booleanfalseIndents the item content.

Shows the selected state for a RadioItem. Displays a checkmark by default.

PropTypeDefaultDescription
No component-specific props. Accepts standard HTML attributes.