Skip to content

Dropdown

A dropdown menu for actions, navigation, and compact option controls.

Source Code

Dropdown is backed by @areia/slots and wires open state, positioning, keyboard navigation, typeahead, item selection, checkbox items, and radio items.

import ilha from "ilha";
import { Button, Dropdown } from "areia";

export default ilha.render(() => (
  <Dropdown trigger={<Button>Add</Button>}>
    <Dropdown.Item value="component">Component</Dropdown.Item>
    <Dropdown.Item value="primitive">Primitive</Dropdown.Item>
    <Dropdown.Item value="example">Example</Dropdown.Item>
  </Dropdown>
));

Import

import { Dropdown } from "areia";

Usage

Prefer composing Dropdown.Trigger and Dropdown.Content as children. The trigger, children, and items props remain available as shortcuts.

import ilha from "ilha";
import { Button, Dropdown } from "areia";

export default ilha.render(() => (
  <Dropdown>
    <Dropdown.Trigger>
      <Button>Menu</Button>
    </Dropdown.Trigger>
    <Dropdown.Content>
      <Dropdown.Item value="edit">Edit</Dropdown.Item>
      <Dropdown.Item value="duplicate">Duplicate</Dropdown.Item>
      <Dropdown.Separator />
      <Dropdown.Item value="delete" variant="danger">
        Delete
      </Dropdown.Item>
    </Dropdown.Content>
  </Dropdown>
));

Dropdown is available as an alias if that reads better in your code.

import ilha from "ilha";
import { Button, Dropdown } from "areia";

export default ilha.render(() => (
  <Dropdown trigger={<Button>Actions</Button>}>
    <Dropdown.Group>
      <Dropdown.Label>Project</Dropdown.Label>
      <Dropdown.Item value="rename">Rename</Dropdown.Item>
      <Dropdown.Item value="duplicate">Duplicate</Dropdown.Item>
    </Dropdown.Group>
    <Dropdown.Separator />
    <Dropdown.Item value="delete" variant="danger">
      Delete
    </Dropdown.Item>
  </Dropdown>
));

Use Dropdown.Static(...) for static markup or when you want to initialize behavior yourself. For most application usage, call Dropdown(...).

Examples

Basic

import ilha from "ilha";
import { Button, Dropdown } from "areia";

export default ilha.render(() => (
  <Dropdown trigger={<Button>Add</Button>}>
    <Dropdown.Item value="component">Component</Dropdown.Item>
    <Dropdown.Item value="primitive">Primitive</Dropdown.Item>
    <Dropdown.Item value="example">Example</Dropdown.Item>
  </Dropdown>
));

Inset Items

Use inset on items without an icon to align their text with selection indicators or icon-bearing items.

import ilha from "ilha";
import { Button, Dropdown } from "areia";

export default ilha.render(() => (
  <Dropdown trigger={<Button>Edit</Button>}>
    <Dropdown.Item value="rename">Rename</Dropdown.Item>
    <Dropdown.Item value="duplicate">Duplicate</Dropdown.Item>
    <Dropdown.Separator />
    <Dropdown.Item value="move" inset>
      Move to folder
    </Dropdown.Item>
    <Dropdown.Item value="favorite" inset>
      Add to favorites
    </Dropdown.Item>
    <Dropdown.Separator />
    <Dropdown.Item value="delete" variant="danger">
      Delete
    </Dropdown.Item>
  </Dropdown>
));

Change Events

Use onSelect on the root to respond to accepted item selections.

import ilha from "ilha";
import { Button, Dropdown } from "areia";

export default ilha
  .state("lastAction", "")
  .render(({ state }) => (
    <div class="flex flex-col items-start gap-3">
      <Dropdown
        trigger={<Button>Actions</Button>} => {
          state.lastAction(value);
        }}
      >
        <Dropdown.Item value="duplicate">
          Duplicate
        </Dropdown.Item>
        <Dropdown.Item value="rename">Rename</Dropdown.Item>
        <Dropdown.Separator />
        <Dropdown.Item value="delete" variant="danger">
          Delete
        </Dropdown.Item>
      </Dropdown>
      <p class="text-sm text-areia-subtle">
        Last action:{" "}
        <span class="text-areia-default">
          {state.lastAction() || "None"}
        </span>
      </p>
    </div>
  ));

Checkbox Items

Use checkbox items for independently toggleable options.

import ilha from "ilha";
import { Button, Dropdown } from "areia";

export default ilha
  .state("visible", ["sidebar", "wrap"])
  .render(({ state }) => (
    <Dropdown
      trigger={<Button>View Options</Button>}
      defaultValues={state.visible()}
      closeOnSelect={false} => {
        state.visible(values);
      }}
    >
      <Dropdown.Group>
        <Dropdown.Label>Display</Dropdown.Label>
        <Dropdown.CheckboxItem
          value="sidebar"
          checked={state.visible().includes("sidebar")}
        >
          Show sidebar
        </Dropdown.CheckboxItem>
        <Dropdown.CheckboxItem
          value="lines"
          checked={state.visible().includes("lines")}
        >
          Show line numbers
        </Dropdown.CheckboxItem>
        <Dropdown.CheckboxItem
          value="wrap"
          checked={state.visible().includes("wrap")}
        >
          Word wrap
        </Dropdown.CheckboxItem>
      </Dropdown.Group>
    </Dropdown>
  ));

Radio Items

Use radio items for a single selected value.

import ilha from "ilha";
import { Button, Dropdown } from "areia";

export default ilha
  .state("density", "comfortable")
  .render(({ state }) => (
    <Dropdown
      trigger={<Button>Density</Button>}
      defaultValue={state.density()}
      closeOnSelect={false} => {
        state.density(value ?? "comfortable");
      }}
    >
      <Dropdown.Label>Table density</Dropdown.Label>
      <Dropdown.RadioItem
        value="compact"
        checked={state.density() === "compact"}
      >
        Compact
      </Dropdown.RadioItem>
      <Dropdown.RadioItem
        value="comfortable"
        checked={state.density() === "comfortable"}
      >
        Comfortable
      </Dropdown.RadioItem>
      <Dropdown.RadioItem
        value="spacious"
        checked={state.density() === "spacious"}
      >
        Spacious
      </Dropdown.RadioItem>
    </Dropdown>
  ));

Custom Trigger

Pass any Areia or Ilha-rendered content as the trigger.

AR
import ilha from "ilha";
import { Dropdown } from "areia";

export default ilha.render(() => (
  <Dropdown
    trigger={
      <span class="flex size-8 items-center justify-center rounded-full bg-areia-accent text-sm font-medium text-white">
        AR
      </span>
    }
    triggerClass="rounded-full"
  >
    <Dropdown.Item value="profile">Profile</Dropdown.Item>
    <Dropdown.Item value="settings">Settings</Dropdown.Item>
    <Dropdown.Separator />
    <Dropdown.Item value="logout" variant="danger">
      Log out
    </Dropdown.Item>
  </Dropdown>
));

Use Dropdown.LinkItem for semantic links.

import ilha from "ilha";
import { Button, Dropdown } from "areia";

export default ilha.render(() => (
  <Dropdown trigger={<Button>Resources</Button>}>
    <Dropdown.LinkItem href="/settings" value="settings">
      Settings
    </Dropdown.LinkItem>
    <Dropdown.LinkItem href="/docs" value="docs">
      Documentation
    </Dropdown.LinkItem>
    <Dropdown.Separator />
    <Dropdown.LinkItem
      href="https://ilha.build/llms.txt"
      external
      value="ilha-llms"
    >
      Ilha llms.txt
    </Dropdown.LinkItem>
  </Dropdown>
));

API Reference

Root component that manages the dropdown state.

PropTypeDefaultDescription
triggerunknown-Content rendered inside the trigger button.
childrenunknown-Composed dropdown slots, or menu content when no Dropdown.Content child is present.
itemsDropdownItemInput[]-Convenience item descriptors. Ignored when children is provided.
defaultOpenbooleanfalseInitial open state.
defaultValuestring | nullnullInitial radio item value.
defaultValuesstring[][]Initial checkbox item values.
closeOnSelectbooleantrueWhether selecting an item closes the menu.
side"top" | "right" | "bottom" | "left""bottom"Preferred side for the popup.
align"start" | "center" | "end""start"Preferred alignment relative to the trigger.
onSelect(value: string) => void-Fires when a user selects an item.
onValueChange(value: string | null) => void-Fires when the radio value changes.
onValuesChange(values: string[]) => void-Fires when checkbox values change.
onOpenChange(open: boolean) => void-Called when open state changes.
onPortalMounted(container: HTMLElement) => void-After portaled menu mounts on open. See Dialog docs, Ilha + portaled overlays.
triggerClassNamestring-Alias for triggerClass.
contentClassNamestring-Alias for contentClass.

Button that opens the dropdown.

Accepts standard button attributes plus class and className.

Popup container for menu content.

Accepts standard div attributes plus class and className.

Individual menu item for actions.

PropTypeDefaultDescription
valuestring-Selection value emitted by events.
childrenunknown-Item content.
labelunknown-Convenience label when using item descriptors.
iconunknown-Content displayed before the label.
shortcutunknown-Content displayed at the end of the item.
variant"default" | "danger""default"Visual style of the item.
selectedbooleanfalseShows the selection indicator initially.
insetbooleanfalseAdds left padding to align text.
disabledbooleanfalseDisables interaction.

A menu item rendered as an anchor.

PropTypeDefaultDescription
hrefstring-URL to navigate to.
externalbooleanfalseAdds target="_blank" and rel="noreferrer".
variant"default" | "danger""default"Visual style of the item.
insetbooleanfalseAdds left padding to align text.

A menu item that toggles membership in the root values array.

PropTypeDefaultDescription
valuestring-Checkbox value.
checkedbooleanfalseInitial checked state and rendered indicator state.
disabledbooleanfalseDisables interaction.

A menu item that sets the root value.

PropTypeDefaultDescription
valuestring-Radio value.
checkedbooleanfalseInitial checked state and rendered indicator state.
disabledbooleanfalseDisables interaction.

Non-interactive label for a group of items.

Wrapper for grouping related labels and items.

Visual divider between menu sections.

Trailing shortcut hint for an item.