> ## Documentation Index
> Fetch the complete documentation index at: https://docs.twenty.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Dropdown

> Action menus, searchable pickers, and form panels with shared presentation and interaction.

export const StoryEmbed = ({storyId, title, height = 240}) => <>
    <Tabs>
      <Tab title="Light">
        <iframe title={`${title} (light)`} src={`https://storybook.twenty.com/iframe.html?id=${storyId}&viewMode=story&globals=colorScheme:light`} width="100%" height={height} loading="lazy" style={{
  border: 0
}} />
      </Tab>
      <Tab title="Dark">
        <iframe title={`${title} (dark)`} src={`https://storybook.twenty.com/iframe.html?id=${storyId}&viewMode=story&globals=colorScheme:dark`} width="100%" height={height} loading="lazy" style={{
  border: 0
}} />
      </Tab>
    </Tabs>
    <a href={`https://storybook.twenty.com/?path=/story/${storyId}`}>
      Open in Storybook
    </a>
  </>;

`Dropdown` combines a positioned popup, visual rows, keyboard navigation, focus, and dismissal. Your application owns data, filtering, fetching, pagination, and selected values. Import it from `twenty-ui/components`.

<StoryEmbed storyId="ui-components-dropdown--documentation" title="Dropdown actions" height={380} />

## Choose an interaction type

Set `type` on `Dropdown.Root` to match the popup's contents.

| Type     | Content                                          | Interaction                                                                                        |
| -------- | ------------------------------------------------ | -------------------------------------------------------------------------------------------------- |
| `menu`   | Commands and navigation links                    | Menu semantics with arrow navigation and typeahead.                                                |
| `picker` | Search, selectable values, and optional commands | A dialog containing a search field and selectable toggle buttons. Arrow keys navigate result rows. |
| `panel`  | Inputs, forms, and supporting actions            | A dialog with native input behavior and Tab navigation.                                            |

Pickers allow creation actions and selectable values in the same list, in any order. Options expose their selected state with `aria-pressed`; commands keep their button semantics. Use a descriptive accessible name on `Content` and `Search`.

The underlying [Menu](/ui/primitives/surfaces/menu), [Select](/ui/primitives/input/select), and [Popover](/ui/primitives/surfaces/popover) remain available for direct composition.

## Anatomy

```text theme={null}
Dropdown.Root
├── Dropdown.Trigger
└── Dropdown.Content
    ├── Dropdown.Search
    └── Dropdown.Section
        ├── Dropdown.ActionItem
        └── Dropdown.OptionItem
```

`Content` includes the portal and positioner. Use `width`, `side`, `align`, `sideOffset`, and `alignOffset` to control placement. `Section` groups rows with shared spacing, and `Separator` divides sections. Rows use [ListItem](/ui/primitives/navigation/list-item) for their appearance.

```tsx theme={null}
import { Dropdown } from 'twenty-ui/components';
import { IconCopy } from 'twenty-ui/icon';
import { Button } from 'twenty-ui/primitives/input';

export const RecordActions = ({ onDuplicate }: { onDuplicate: () => void }) => (
  <Dropdown.Root type="menu">
    <Dropdown.Trigger render={<Button>Record actions</Button>} />
    <Dropdown.Content aria-label="Record actions">
      <Dropdown.Section>
        <Dropdown.ActionItem startIcon={<IconCopy />} onClick={onDuplicate}>
          Duplicate
        </Dropdown.ActionItem>
      </Dropdown.Section>
    </Dropdown.Content>
  </Dropdown.Root>
);
```

Pass custom trigger elements through `render` so the trigger remains one interactive element. Custom components must forward the supplied props and ref. `ActionItem` also accepts `render` for links.

## Search and selection

`Search` provides the input and navigation into the result rows. Pass `value` and `onValueChange`; compute or fetch matching results in your application. Render `Loading` while a request is pending and `Empty` when no results match. Both accept caller-provided status text.

<StoryEmbed storyId="ui-components-dropdown--picker" title="Searchable picker with inline creation" height={420} />

```tsx theme={null}
import { useState } from 'react';
import { Dropdown } from 'twenty-ui/components';
import { Button } from 'twenty-ui/primitives/input';

export const PersonPicker = () => {
  const [search, setSearch] = useState('');
  const [selectedPerson, setSelectedPerson] = useState('');
  const people = ['Ada Lovelace', 'Grace Hopper'].filter((person) =>
    person.toLowerCase().includes(search.toLowerCase()),
  );

  return (
    <Dropdown.Root type="picker">
      <Dropdown.Trigger render={<Button>Choose person</Button>} />
      <Dropdown.Content aria-label="Choose person">
        <Dropdown.Search
          aria-label="Search people"
          value={search}
          onValueChange={setSearch}
        />
        <Dropdown.Section>
          {people.map((person) => (
            <Dropdown.OptionItem
              key={person}
              selected={selectedPerson === person}
              onSelect={() => setSelectedPerson(person)}
            >
              {person}
            </Dropdown.OptionItem>
          ))}
        </Dropdown.Section>
      </Dropdown.Content>
    </Dropdown.Root>
  );
};
```

Actions and single selection close by default. Set `multiple` on the root to keep option selection open. The application still owns the selected values. `closeOnClick` on actions and `closeOnSelect` on options override those defaults, including repeated actions or asynchronous progress. Use `disabled` to prevent activation while retaining the row's appearance.

<StoryEmbed storyId="ui-components-dropdown--multiple-selection" title="Multiple selection" height={420} />

## Pages and submenus

Use `Page` for navigation that replaces the contents of the same popup. Wrap the first page in `Page id="root"`, or choose another initial page with `defaultPage`. Set `page` on an action to navigate without closing. `Back` returns to the previous page and restores focus to its invoking row. Closing the dropdown resets its page history.

A page can override the root's `type`. For example, an action menu can open a searchable picker page or a form panel. Search receives focus when entering a searchable page.

<StoryEmbed storyId="ui-components-dropdown--pages" title="Menu to picker page navigation" height={420} />

```tsx theme={null}
import { Dropdown } from 'twenty-ui/components';
import { Button } from 'twenty-ui/primitives/input';

export const FilterMenu = () => (
  <Dropdown.Root type="menu">
    <Dropdown.Trigger render={<Button>Filters</Button>} />
    <Dropdown.Content aria-label="Filters">
      <Dropdown.Page id="root">
        <Dropdown.Section>
          <Dropdown.ActionItem page="status">Status</Dropdown.ActionItem>
        </Dropdown.Section>
      </Dropdown.Page>
      <Dropdown.Page id="status" type="picker">
        <Dropdown.Back>Filters</Dropdown.Back>
        <Dropdown.Section>
          <Dropdown.OptionItem selected={false}>Active</Dropdown.OptionItem>
          <Dropdown.OptionItem selected={false}>Archived</Dropdown.OptionItem>
        </Dropdown.Section>
      </Dropdown.Page>
    </Dropdown.Content>
  </Dropdown.Root>
);
```

Use `Submenu` with `SubmenuTrigger` and a nested `Content` for a side-by-side menu. Arrow Right enters the submenu; Arrow Left returns to its parent. Selecting a closing action closes the whole dropdown.

```text theme={null}
Dropdown.Content
└── Dropdown.Submenu
    ├── Dropdown.SubmenuTrigger
    └── Dropdown.Content
        └── Dropdown.ActionItem
```

## Panels and controlled state

Use `type="panel"` for form inputs. Input interaction keeps the panel open; a save action can close it. Own `open` and `onOpenChange` when another component or a shortcut needs to open the dropdown. Keep that state with the feature that coordinates the interaction.

<StoryEmbed storyId="ui-components-dropdown--panel" title="Controlled form panel" height={380} />

Escape dismisses the popup and restores focus to its trigger. Outside interaction also dismisses it. `Content` accepts focus and portal options from Popover, including an explicit return-focus target for editor workflows.

## Props

### Dropdown.Root

<ParamField body="Root.defaultOpen" type="boolean" />

<ParamField body="Root.defaultPage" type="string" />

<ParamField body="Root.multiple" type="boolean" />

<ParamField body="Root.onOpenChange" type="((open: boolean) => void)" />

<ParamField body="Root.open" type="boolean" />

<ParamField body="Root.type" type="&#x22;menu&#x22; | &#x22;panel&#x22; | &#x22;picker&#x22;" required />

### Dropdown.Trigger

<ParamField body="Trigger.className" type="string | ((state: PopoverTriggerState) => string | undefined)">
  CSS class applied to the element, or a function that
  returns a class based on the component's state.
</ParamField>

<ParamField body="Trigger.closeDelay" type="number" default="0">
  How long to wait before closing the popover that was opened on hover.
  Specified in milliseconds.

  Requires the `openOnHover` prop.
</ParamField>

<ParamField body="Trigger.delay" type="number" default="300">
  How long to wait before the popover may be opened on hover. Specified in milliseconds.

  Requires the `openOnHover` prop.
</ParamField>

<ParamField body="Trigger.handle" type="PopoverHandle<unknown>">
  A handle to associate the trigger with a popover.
</ParamField>

<ParamField body="Trigger.id" type="string">
  ID of the trigger. In addition to being forwarded to the rendered element,
  it is also used to specify the active trigger for the popover in controlled mode (with the PopoverRoot `triggerId` prop).
</ParamField>

<ParamField body="Trigger.nativeButton" type="boolean" default="true">
  Whether the component renders a native `<button>` element when replacing it
  via the `render` prop.
  Set to `false` if the rendered element is not a button (e.g. `<div>`).
</ParamField>

<ParamField body="Trigger.openOnHover" type="boolean" default="false">
  Whether the popover should also open when the trigger is hovered.
</ParamField>

<ParamField body="Trigger.payload" type="unknown">
  A payload to pass to the popover when it is opened.
</ParamField>

<ParamField body="Trigger.render" type="ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, PopoverTriggerState>">
  Allows you to replace the component's HTML element
  with a different tag, or compose it with another component.

  Accepts a `ReactElement` or a function that returns the element to render.
</ParamField>

<ParamField body="Trigger.style" type="CSSProperties | ((state: PopoverTriggerState) => CSSProperties | undefined)">
  Style applied to the element, or a function that
  returns a style object based on the component's state.
</ParamField>

### Dropdown.Content

<ParamField body="Content.align" type="&#x22;center&#x22; | &#x22;end&#x22; | &#x22;start&#x22;" default="start">
  Alignment of the popup along the anchor.
</ParamField>

<ParamField body="Content.alignOffset" type="number">
  Offset in pixels along the alignment axis.
</ParamField>

<ParamField body="Content.anchor" type="Element | VirtualElement | RefObject<Element | null> | (() => Element | VirtualElement | null) | null">
  Element or position the popup is anchored to. Defaults to the trigger.
</ParamField>

<ParamField body="Content.className" type="string | ((state: PopoverPopupState) => string | undefined)">
  CSS class applied to the element, or a function that
  returns a class based on the component's state.
</ParamField>

<ParamField body="Content.container" type="HTMLElement | ShadowRoot | RefObject<HTMLElement | ShadowRoot | null> | null">
  Element the popup is portaled into. Defaults to the theme's portal
  container.
</ParamField>

<ParamField body="Content.finalFocus" type="boolean | RefObject<HTMLElement | null> | ((closeType: InteractionType) => boolean | void | HTMLElement | null)">
  Determines the element to focus when the popover is closed.

  * `false`: Do not move focus.
  * `true`: Move focus based on the default behavior (trigger or previously focused element).
  * `RefObject`: Move focus to the ref element.
  * `function`: Called with the interaction type (`mouse`, `touch`, `pen`, or `keyboard`).
    Return an element to focus, `true` to use the default behavior, `null` to fall back to the default behavior, or `false`/`undefined` to do nothing.
</ParamField>

<ParamField body="Content.initialFocus" type="boolean | RefObject<HTMLElement | null> | ((openType: InteractionType) => boolean | void | HTMLElement | null)">
  Determines the element to focus when the popover is opened.
  By default, focus moves to the first tabbable element inside the popup, except when the popover
  is opened by touch — then the popup itself is focused to avoid opening the virtual keyboard.

  * `false`: Do not move focus.
  * `true`: Move focus based on the default behavior (first tabbable element or popup).
  * `RefObject`: Move focus to the ref element.
  * `function`: Called with the interaction type (`mouse`, `touch`, `pen`, or `keyboard`).
    Return an element to focus, `true` to use the default behavior, `null` to fall back to the default behavior, or `false`/`undefined` to do nothing.
</ParamField>

<ParamField body="Content.keepMounted" type="boolean">
  Keeps the popup mounted in the DOM while the popover is closed.
</ParamField>

<ParamField body="Content.render" type="ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, PopoverPopupState>">
  Allows you to replace the component's HTML element
  with a different tag, or compose it with another component.

  Accepts a `ReactElement` or a function that returns the element to render.
</ParamField>

<ParamField body="Content.side" type="&#x22;bottom&#x22; | &#x22;inline-end&#x22; | &#x22;inline-start&#x22; | &#x22;left&#x22; | &#x22;right&#x22; | &#x22;top&#x22;">
  Side of the anchor the popup is placed on.
</ParamField>

<ParamField body="Content.sideOffset" type="number" default="0">
  Distance in pixels between the anchor and the popup.
</ParamField>

<ParamField body="Content.style" type="CSSProperties | ((state: PopoverPopupState) => CSSProperties | undefined)">
  Style applied to the element, or a function that
  returns a style object based on the component's state.
</ParamField>

<ParamField body="Content.width" type="Width<string | number>" default="200" />

### Dropdown.ActionItem

<ParamField body="ActionItem.className" type="string" />

<ParamField body="ActionItem.closeOnClick" type="boolean" default="true" />

<ParamField body="ActionItem.color" type="&#x22;danger&#x22; | &#x22;neutral&#x22;">
  Color of the text and icons. `danger` marks a destructive action.
</ParamField>

<ParamField body="ActionItem.description" type="ReactNode">
  Supporting text, placed according to `descriptionPlacement`.
</ParamField>

<ParamField body="ActionItem.descriptionPlacement" type="&#x22;end&#x22; | &#x22;inline&#x22;">
  Where the description renders: inline after the content or at the end of
  the row.
</ParamField>

<ParamField body="ActionItem.endIcon" type="ReactNode">
  Icon rendered after the content.
</ParamField>

<ParamField body="ActionItem.focusableWhenDisabled" type="boolean" default="false">
  Whether the button should be focusable when disabled.
</ParamField>

<ParamField body="ActionItem.hasSubmenu" type="boolean">
  Shows a chevron indicating that the item opens a submenu.
</ParamField>

<ParamField body="ActionItem.hotkeys" type="string[]">
  Keyboard shortcut keys displayed at the end of the row. Registering the
  shortcut is up to the application.
</ParamField>

<ParamField body="ActionItem.nativeButton" type="boolean" default="true when render is omitted; false otherwise">
  Whether the component renders a native `<button>` element when replacing it
  via the `render` prop.
  Set to `false` if the rendered element is not a button (for example, `<div>`).
</ParamField>

<ParamField body="ActionItem.page" type="string" />

<ParamField body="ActionItem.render" type="ReactElement<unknown, string | JSXElementConstructor<any>>" />

<ParamField body="ActionItem.startIcon" type="ReactNode">
  Icon rendered before the content.
</ParamField>

<ParamField body="ActionItem.style" type="CSSProperties" />

### Dropdown.OptionItem

<ParamField body="OptionItem.className" type="string" />

<ParamField body="OptionItem.closeOnSelect" type="boolean" />

<ParamField body="OptionItem.color" type="&#x22;danger&#x22; | &#x22;neutral&#x22;">
  Color of the text and icons. `danger` marks a destructive action.
</ParamField>

<ParamField body="OptionItem.description" type="ReactNode">
  Supporting text, placed according to `descriptionPlacement`.
</ParamField>

<ParamField body="OptionItem.descriptionPlacement" type="&#x22;end&#x22; | &#x22;inline&#x22;">
  Where the description renders: inline after the content or at the end of
  the row.
</ParamField>

<ParamField body="OptionItem.endIcon" type="ReactNode">
  Icon rendered after the content.
</ParamField>

<ParamField body="OptionItem.focusableWhenDisabled" type="boolean" default="false">
  Whether the button should be focusable when disabled.
</ParamField>

<ParamField body="OptionItem.hasSubmenu" type="boolean">
  Shows a chevron indicating that the item opens a submenu.
</ParamField>

<ParamField body="OptionItem.hotkeys" type="string[]">
  Keyboard shortcut keys displayed at the end of the row. Registering the
  shortcut is up to the application.
</ParamField>

<ParamField body="OptionItem.nativeButton" type="boolean" default="true when render is omitted; false otherwise">
  Whether the component renders a native `<button>` element when replacing it
  via the `render` prop.
  Set to `false` if the rendered element is not a button (for example, `<div>`).
</ParamField>

<ParamField body="OptionItem.onSelect" type="(() => void)" />

<ParamField body="OptionItem.render" type="ReactElement<unknown, string | JSXElementConstructor<any>>" />

<ParamField body="OptionItem.selected" type="boolean" required />

<ParamField body="OptionItem.startIcon" type="ReactNode">
  Icon rendered before the content.
</ParamField>

<ParamField body="OptionItem.style" type="CSSProperties" />

### Dropdown.Search

<ParamField body="Search.className" type="string | ((state: InputState) => string | undefined)">
  CSS class applied to the element, or a function that
  returns a class based on the component's state.
</ParamField>

<ParamField body="Search.defaultValue" type="string | number | readonly string[]">
  The default value of the input. Use when uncontrolled.
</ParamField>

<ParamField body="Search.onValueChange" type="((value: string) => void)" />

<ParamField body="Search.render" type="ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, InputState>">
  Allows you to replace the component's HTML element
  with a different tag, or compose it with another component.

  Accepts a `ReactElement` or a function that returns the element to render.
</ParamField>

<ParamField body="Search.style" type="CSSProperties | ((state: InputState) => CSSProperties | undefined)">
  Style applied to the element, or a function that
  returns a style object based on the component's state.
</ParamField>

<ParamField body="Search.value" type="string | number | readonly string[]">
  The value of the input. Use when controlled.
</ParamField>

### Dropdown.Page

<ParamField body="Page.id" type="string" required />

<ParamField body="Page.type" type="&#x22;menu&#x22; | &#x22;panel&#x22; | &#x22;picker&#x22;" />

### Dropdown.Back

<ParamField body="Back.className" type="string" />

<ParamField body="Back.color" type="&#x22;danger&#x22; | &#x22;neutral&#x22;">
  Color of the text and icons. `danger` marks a destructive action.
</ParamField>

<ParamField body="Back.description" type="ReactNode">
  Supporting text, placed according to `descriptionPlacement`.
</ParamField>

<ParamField body="Back.descriptionPlacement" type="&#x22;end&#x22; | &#x22;inline&#x22;">
  Where the description renders: inline after the content or at the end of
  the row.
</ParamField>

<ParamField body="Back.endIcon" type="ReactNode">
  Icon rendered after the content.
</ParamField>

<ParamField body="Back.focusableWhenDisabled" type="boolean" default="false">
  Whether the button should be focusable when disabled.
</ParamField>

<ParamField body="Back.hasSubmenu" type="boolean">
  Shows a chevron indicating that the item opens a submenu.
</ParamField>

<ParamField body="Back.hotkeys" type="string[]">
  Keyboard shortcut keys displayed at the end of the row. Registering the
  shortcut is up to the application.
</ParamField>

<ParamField body="Back.nativeButton" type="boolean" default="true when render is omitted; false otherwise">
  Whether the component renders a native `<button>` element when replacing it
  via the `render` prop.
  Set to `false` if the rendered element is not a button (for example, `<div>`).
</ParamField>

<ParamField body="Back.render" type="ReactElement<unknown, string | JSXElementConstructor<any>>" />

<ParamField body="Back.startIcon" type="ReactNode">
  Icon rendered before the content.
</ParamField>

<ParamField body="Back.style" type="CSSProperties" />

### Dropdown.Submenu

<ParamField body="Submenu.defaultOpen" type="boolean" />

<ParamField body="Submenu.defaultPage" type="string" />

<ParamField body="Submenu.multiple" type="boolean" />

<ParamField body="Submenu.onOpenChange" type="((open: boolean) => void)" />

<ParamField body="Submenu.open" type="boolean" />

<ParamField body="Submenu.type" type="&#x22;menu&#x22; | &#x22;panel&#x22; | &#x22;picker&#x22;" default="menu" />

### Dropdown.SubmenuTrigger

<ParamField body="SubmenuTrigger.className" type="string" />

<ParamField body="SubmenuTrigger.closeDelay" type="number" />

<ParamField body="SubmenuTrigger.color" type="&#x22;danger&#x22; | &#x22;neutral&#x22;">
  Color of the text and icons. `danger` marks a destructive action.
</ParamField>

<ParamField body="SubmenuTrigger.delay" type="number" />

<ParamField body="SubmenuTrigger.description" type="ReactNode">
  Supporting text, placed according to `descriptionPlacement`.
</ParamField>

<ParamField body="SubmenuTrigger.descriptionPlacement" type="&#x22;end&#x22; | &#x22;inline&#x22;">
  Where the description renders: inline after the content or at the end of
  the row.
</ParamField>

<ParamField body="SubmenuTrigger.endIcon" type="ReactNode">
  Icon rendered after the content.
</ParamField>

<ParamField body="SubmenuTrigger.focusableWhenDisabled" type="boolean" default="false">
  Whether the button should be focusable when disabled.
</ParamField>

<ParamField body="SubmenuTrigger.hasSubmenu" type="boolean" default="true">
  Shows a chevron indicating that the item opens a submenu.
</ParamField>

<ParamField body="SubmenuTrigger.hotkeys" type="string[]">
  Keyboard shortcut keys displayed at the end of the row. Registering the
  shortcut is up to the application.
</ParamField>

<ParamField body="SubmenuTrigger.nativeButton" type="boolean" default="true when render is omitted; false otherwise">
  Whether the component renders a native `<button>` element when replacing it
  via the `render` prop.
  Set to `false` if the rendered element is not a button (for example, `<div>`).
</ParamField>

<ParamField body="SubmenuTrigger.openOnHover" type="boolean" default="true" />

<ParamField body="SubmenuTrigger.render" type="ReactElement<unknown, string | JSXElementConstructor<any>>" />

<ParamField body="SubmenuTrigger.startIcon" type="ReactNode">
  Icon rendered before the content.
</ParamField>

<ParamField body="SubmenuTrigger.style" type="CSSProperties" />

### Dropdown.Section

<ParamField body="Section.label" type="ReactNode" />
