Dropdown Menu
@cloudflare/kumo
import { DropdownMenu, Button } from "@cloudflare/kumo";
import { PlusIcon } from "@phosphor-icons/react";

export function DropdownBasicDemo() {
  return (
    <DropdownMenu>
      <DropdownMenu.Trigger render={<Button icon={PlusIcon}>Add</Button>} />
      <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 "@cloudflare/kumo";

Granular

import { DropdownMenu } from "@cloudflare/kumo/components/dropdown";

Usage

import { DropdownMenu, Button } from "@cloudflare/kumo";

export default function Example() {
  return (
    <DropdownMenu>
      <DropdownMenu.Trigger render={<Button>Menu</Button>} />
      <DropdownMenu.Content>
        <DropdownMenu.Item>Option 1</DropdownMenu.Item>
        <DropdownMenu.Item>Option 2</DropdownMenu.Item>
      </DropdownMenu.Content>
    </DropdownMenu>
  );
}

Examples

Basic Dropdown

import { DropdownMenu, Button } from "@cloudflare/kumo";
import { PlusIcon } from "@phosphor-icons/react";

export function DropdownBasicDemo() {
  return (
    <DropdownMenu>
      <DropdownMenu.Trigger render={<Button icon={PlusIcon}>Add</Button>} />
      <DropdownMenu.Content>
        <DropdownMenu.Item>Worker</DropdownMenu.Item>
        <DropdownMenu.Item>Pages</DropdownMenu.Item>
        <DropdownMenu.Item>KV Namespace</DropdownMenu.Item>
      </DropdownMenu.Content>
    </DropdownMenu>
  );
}

Checkbox Items

Use DropdownMenu.CheckboxItem for toggleable options that can be independently checked or unchecked.

import { useState } from "react";
import { DropdownMenu, Button } from "@cloudflare/kumo";

export function DropdownCheckboxDemo() {
  const [showSidebar, setShowSidebar] = useState(true);
  const [showLineNumbers, setShowLineNumbers] = useState(false);
  const [wordWrap, setWordWrap] = useState(true);

  return (
    <DropdownMenu>
      <DropdownMenu.Trigger render={<Button>View Options</Button>} />
      <DropdownMenu.Content>
        <DropdownMenu.Group>
          <DropdownMenu.Label>Display</DropdownMenu.Label>
          <DropdownMenu.CheckboxItem
            checked={showSidebar}
            onCheckedChange={setShowSidebar}
          >
            Show sidebar
          </DropdownMenu.CheckboxItem>
          <DropdownMenu.CheckboxItem
            checked={showLineNumbers}
            onCheckedChange={setShowLineNumbers}
          >
            Show line numbers
          </DropdownMenu.CheckboxItem>
          <DropdownMenu.CheckboxItem
            checked={wordWrap}
            onCheckedChange={setWordWrap}
          >
            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.

import { useState } from "react";
import { DropdownMenu, Button } from "@cloudflare/kumo";
import { UserIcon, CreditCardIcon, MoonIcon, SignOutIcon } from "@phosphor-icons/react";

export function DropdownNestedDemo() {
  const [language, setLanguage] = useState("en");
  const [timezone, setTimezone] = useState("America/Los_Angeles");

  return (
    <DropdownMenu>
      <DropdownMenu.Trigger render={<Button icon={UserIcon}>Account</Button>} />
      <DropdownMenu.Content>
        <DropdownMenu.Item icon={UserIcon}>Profile</DropdownMenu.Item>
        <DropdownMenu.Item icon={CreditCardIcon}>Billing</DropdownMenu.Item>
        <DropdownMenu.Item icon={MoonIcon}>Dark mode</DropdownMenu.Item>

        {/* Language submenu with RadioGroup */}
        <DropdownMenu.Sub>
          <DropdownMenu.SubTrigger>Language</DropdownMenu.SubTrigger>
          <DropdownMenu.SubContent>
            <DropdownMenu.Group>
              <DropdownMenu.RadioGroup
                value={language}
                onValueChange={setLanguage}
              >
                {languages.map((lang) => (
                  <DropdownMenu.RadioItem key={lang.code} value={lang.code}>
                    {lang.label}
                    <DropdownMenu.RadioItemIndicator />
                  </DropdownMenu.RadioItem>
                ))}
              </DropdownMenu.RadioGroup>
            </DropdownMenu.Group>
          </DropdownMenu.SubContent>
        </DropdownMenu.Sub>

        {/* Timezone submenu with RadioGroup */}
        <DropdownMenu.Sub>
          <DropdownMenu.SubTrigger>Set Timezone</DropdownMenu.SubTrigger>
          <DropdownMenu.SubContent>
            <DropdownMenu.Group>
              <DropdownMenu.RadioGroup
                value={timezone}
                onValueChange={setTimezone}
              >
                {timezones.map((tz) => (
                  <DropdownMenu.RadioItem key={tz.value} value={tz.value}>
                    {tz.label}
                    <DropdownMenu.RadioItemIndicator />
                  </DropdownMenu.RadioItem>
                ))}
              </DropdownMenu.RadioGroup>
            </DropdownMenu.Group>
          </DropdownMenu.SubContent>
        </DropdownMenu.Sub>

        <DropdownMenu.Separator />
        <DropdownMenu.Item icon={SignOutIcon} 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.

import { DropdownMenu, Button } from "@cloudflare/kumo";
import { GearIcon, BookOpenIcon, ArrowSquareOutIcon } from "@phosphor-icons/react";

/**
 * Demonstrates the new LinkItem component for navigation links.
 * Use LinkItem instead of Item with href for cleaner, more semantic links.
 */
export function DropdownLinkItemDemo() {
  return (
    <DropdownMenu>
      <DropdownMenu.Trigger render={<Button>Resources</Button>} />
      <DropdownMenu.Content>
        <DropdownMenu.LinkItem href="/settings" icon={GearIcon}>
          Settings
        </DropdownMenu.LinkItem>
        <DropdownMenu.LinkItem href="/docs" icon={BookOpenIcon}>
          Documentation
        </DropdownMenu.LinkItem>
        <DropdownMenu.Separator />
        <DropdownMenu.LinkItem
          href="https://developers.cloudflare.com"
          target="_blank"
          icon={ArrowSquareOutIcon}
        >
          Developer Docs
        </DropdownMenu.LinkItem>
      </DropdownMenu.Content>
    </DropdownMenu>
  );
}

API Reference

Root component that manages the dropdown state.

PropTypeDefaultDescription
variant"default" | "danger""default"Visual style of the dropdown item. - `"default"` — Standard item appearance - `"danger"` — Destructive action with red text

Button that opens the dropdown when clicked.

PropTypeDefault

No component-specific props. Accepts standard HTML attributes.

Individual menu item for actions.

PropTypeDefault

No component-specific props. Accepts standard HTML attributes.

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

PropTypeDefault

No component-specific props. Accepts standard HTML attributes.

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

PropTypeDefault

No component-specific props. Accepts standard HTML attributes.

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

PropTypeDefault

No component-specific props. Accepts standard HTML attributes.

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

PropTypeDefault

No component-specific props. Accepts standard HTML attributes.

Container for submenu items. Positioned relative to the SubTrigger.

PropTypeDefault

No component-specific props. Accepts standard HTML attributes.

A visual divider between menu items.

PropTypeDefault

No component-specific props. Accepts standard HTML attributes.

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

PropTypeDefault

No component-specific props. Accepts standard HTML attributes.

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

PropTypeDefault

No component-specific props. Accepts standard HTML attributes.

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

PropTypeDefault

No component-specific props. Accepts standard HTML attributes.