> ## 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, and the click that dismisses it doesn't activate what it lands on. Keys pressed inside the popup stay inside it, except Ctrl or Cmd shortcuts the dropdown doesn't handle, so page-level shortcuts keep working. `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" />
