---
title: Dropmenu
description: "A dropdown menu for selecting a single option from a list, with support for search, custom styling, and controlled/uncontrolled usage."
type: component
section: Components
order: 9
---

A dropdown menu for selecting a single option from a list, with support for search, custom styling, and controlled/uncontrolled usage.

---

## Import

```tsx
import { Dropmenu } from "asheeui";
```

---

## Usage

```tsx
"use client";

import { useState } from "react";
import { Dropmenu } from "asheeui";

const options = [
	{ label: "Option 1", value: "1" },
	{ label: "Option 2", value: "2" },
	{ label: "Option 3", value: "3" },
];

export default function SelectBasic() {
	const [value, setValue] = useState<string | number>("");

	return (
		<div className="w-full max-w-md">
			<Dropmenu
				value={value}
				onValueChange={setValue}
				options={options}
				placeholder="Choose an option..."
			/>
		</div>
	);
}
```

---

## Examples

### Controlled

<ComponentPreview path="dropmenu/controlled" />

### With default value

<ComponentPreview path="dropmenu/default-value" />

### With search

<ComponentPreview path="dropmenu/search" />

### With below list content

<ComponentPreview path="dropmenu/below-list" />

### Menu styling

<ComponentPreview path="dropmenu/menu-styling" />

---

## Inheritance

Dropmenu shares common foundational props with [`<Input />`](/docs/components/input), including:

- `size`, `radius`, `variant`, `color`
- `label`, `labelAlign`, `description`, `message`
- `status`, `required`, `isLoading`, `disabled`
- `className`, and all native field attributes

> **Reference**: For a complete list of inherited props with detailed descriptions, see the [Input documentation](/docs/components/input#props).

### What Dropmenu adds

Dropmenu extends Input with the following additional features:

| Feature | Description |
|---|---|
| **Options List** | A dropdown menu showing selectable options |
| **Single Selection** | Selection handling with `onValueChange` callback |
| **Search Filtering** | Optional real-time search to filter options |
| **Below List Content** | Ability to render custom content below the options list |
| **Menu Configuration** | Full control over dropdown menu appearance and behavior |

### What Dropmenu modifies

| Modification | Description |
|---|---|
| **Trigger** | Uses a Button component instead of an input field |
| **`startContent`** | Available as a prop for the trigger button |
| **`endContent`** | Available as a prop for the trigger button |
| **`onChange`** | Native select change event with synthetic event |
| **`value`** | Controlled selected value for single selection |

### What Dropmenu does not inherit

The following Input props are **not available** on Dropmenu:

| Prop | Reason |
|---|---|
| `type` | Not applicable - Dropmenu uses a button trigger |
| `placeholder` | Replaced with `placeholder` for the trigger button |
| `autoComplete` | Not applicable |
| `inputMode` | Not applicable |
| `pattern` | Not applicable |

> **All other Input props** are fully supported. See the [Input documentation](/docs/components/input#props) for the complete list.

---

## Props

| Prop | Type | Default | Description |
|---|---|---|---|
| `options` | `SelectMenuOption[]` | None | List of selectable options (required) |
| `value` | `string \| number` | None | Controlled selected value |
| `onValueChange` | `(value: string \| number) => void` | None | Callback when selection changes |
| `onChange` | `(e: React.ChangeEvent<HTMLSelectElement>) => void` | None | Native select change event callback |
| `initialValue` | `string \| number` | None | Default value for uncontrolled usage |
| `placeholder` | `string` | `"Dropmenu..."` | Placeholder text when no selection |
| `isSearch` | `boolean` | `false` | Enable search in dropdown |
| `searchPlaceholder` | `string` | `"Search options..."` | Search input placeholder |
| `searchInputName` | `string` | `"select-search"` | Name attribute for search input |
| `belowList` | `ReactNode` | None | Content rendered below options list |
| `name` | `string` | None | Name attribute for the select |
| `startContent` | `ReactNode` | None | Content at the start of the trigger button |
| `endContent` | `ReactNode` | None | Content at the end of the trigger button |
| `menu` | `MenuConfig` | None | Menu configuration overrides (see below) |

### SelectMenuOption

| Prop | Type | Description |
|---|---|---|
| `label` | `string` | Display text in the dropdown |
| `value` | `string \| number` | Unique value for the option |
| `disabled` | `boolean` | Prevents selection of this option |
| `[key: string]` | `unknown` | Additional custom properties |

### Menu Configuration

The dropdown menu appearance is controlled through the `menu` prop, which accepts all [`MenuConfig`](/docs/components/select-menu#props) options:

| Prop | Type | Default | Description |
|---|---|---|---|
| `menu.radius` | `Radius` | `"md"`* | Corner rounding of the dropdown menu |
| `menu.size` | `Size` | `"md"`* | Size of menu items |
| `menu.itemVariant` | `Variant` | `"ghost"`* | Visual style of inactive options |
| `menu.itemColor` | `Color` | `"primary"`* | Color of inactive options |
| `menu.activeItemVariant` | `Variant` | `"faded"`* | Visual style of selected option |
| `menu.activeItemColor` | `Color` | `"primary"`* | Color of selected option |
| `menu.lockScroll` | `boolean` | `false`* | Whether to lock body scroll when open |
| `menu.portal` | `boolean` | `true`* | Whether to render menu in a portal |
| `menu.portalTarget` | `HTMLElement \| null` | `null`* | Custom portal target element |
| `menu.className` | `string` | None | Extra classes for the dropdown menu |

\* *Falls back through Global Configuration if not set. See below.*

---

## Global Configuration

Dropmenu reads defaults from four places, in this order of precedence:

1. **Instance prop**: set directly on `<Dropmenu />`
2. **Component config**: `components.select` in your `ashee.config`
3. **Theme default**: `defaultVariant` / `defaultColor` / `defaultRadius` in your `ashee.config`
4. **Built-in fallback**: component's internal default values

### Component config

```ts
// ashee.config.ts
import type { ExternalConfig } from "asheeui";

export const config: ExternalConfig = {
  components: {
    select: {
      size: "md",
      radius: "md",
      variant: "bordered",
      color: "primary",
      labelAlign: "left",
      menu: {
        itemVariant: "ghost",
        itemColor: "primary",
        activeItemVariant: "faded",
        activeItemColor: "primary",
        radius: "md",
        size: "md",
        portal: true,
        lockScroll: false,
      },
    },
  },
};
```

### Built-in fallbacks

```ts
{
  size: "md",
  radius: "md",
  variant: "bordered",
  color: "primary",
  status: "default",
  labelAlign: "left",
  menu: {
    itemVariant: "ghost",
    itemColor: "primary",
    activeItemVariant: "faded",
    activeItemColor: "primary",
    radius: "md",
    size: "md",
    portal: true,
    lockScroll: false,
  },
}
```

---

## Accessibility

- Renders a native button trigger with `role="listbox"` and `aria-haspopup`
- Uses `aria-expanded` to indicate dropdown state
- Uses `aria-invalid` for error states
- Uses `aria-disabled` for disabled state
- Supports keyboard navigation through options
- Search input is accessible via standard input semantics
- Implements `focus-visible` rings for keyboard navigation

---

## Portal Behavior

The Dropmenu dropdown menu is rendered in a React portal by default. This means the menu is attached to `document.body` rather than staying in the component's DOM hierarchy.

### Why use a portal?

- **Escapes CSS containment**: The menu appears above other content even when inside containers with `overflow: hidden` or `contain: layout`
- **Avoids stacking context issues**: The menu maintains proper z-index regardless of parent stacking contexts
- **Works with any parent**: The menu functions correctly regardless of where the Dropmenu is placed in the component tree
- **Prevents clipping**: The menu is never clipped by parent containers

### When to disable the portal

You may want to disable the portal (by setting `menu.portal={false}`) when:
- You need the menu to stay within a specific container for testing purposes
- You are rendering inside a shadow DOM or iframe where `document.body` is not appropriate
- You have specific layout requirements that depend on the menu remaining in the DOM hierarchy

You can also provide a custom `menu.portalTarget` to render the menu into a specific container instead of `document.body`.

---

## Notes

- **Controlled vs Uncontrolled**: Use `value`/`onValueChange` for controlled usage, or `initialValue` for uncontrolled.
- **Search**: Enable search with `isSearch={true}`. The search input filters options in real-time.
- **Placeholder**: The `placeholder` text is shown when no option is selected.
- **Below List**: The `belowList` prop is useful for adding "Add new" buttons or additional controls.
- **Menu Styling**: The menu appearance can be customized independently from the trigger using the `menu` prop.
- **Portal**: The dropdown menu is portaled to `document.body` by default. This can be disabled via the `menu.portal` prop or component config.
- **Inheritance**: Dropmenu inherits most Input props but uses a Button as the trigger.