Skip to content

Dropdown

A button that opens a list of options to choose from, like a native select. Use it for one or more choices from a known list; add search when the list is long.

Code
demo.tsx
import {Dropdown, Stack} from '@earendil-works/elanor-react';

const projects = ['Website redesign', 'Design system', 'Release notes'];

export default function DropdownDemo() {
  return (
    <Dropdown.Root>
      <Stack gap="small">
        <Dropdown.Label>Project</Dropdown.Label>
        <Dropdown.Trigger>
          <Dropdown.Value placeholder="Select a project" />
          <Dropdown.Icon />
        </Dropdown.Trigger>
      </Stack>
      <Dropdown.Portal>
        <Dropdown.Positioner>
          <Dropdown.Popup>
            <Dropdown.List>
              {projects.map((project) => (
                <Dropdown.Item key={project} value={project}>
                  {project}
                </Dropdown.Item>
              ))}
            </Dropdown.List>
          </Dropdown.Popup>
        </Dropdown.Positioner>
      </Dropdown.Portal>
    </Dropdown.Root>
  );
}

Usage

import {Dropdown} from '@earendil-works/elanor-react';

<Dropdown.Root>
  <Dropdown.Label />
  <Dropdown.Trigger>
    <Dropdown.Value />
    <Dropdown.Icon />
  </Dropdown.Trigger>
  <Dropdown.Portal>
    <Dropdown.Positioner>
      <Dropdown.Popup>
        <Dropdown.List>
          <Dropdown.Item />
        </Dropdown.List>
      </Dropdown.Popup>
    </Dropdown.Positioner>
  </Dropdown.Portal>
</Dropdown.Root>;

Value shows the selected value. When values differ from their labels, pass items to Root, such as items={[{value: 'alpha', label: 'Alpha'}]}.

The trigger stays on one line and truncates long values. Wrap its text in Value or an element, such as a span; bare text doesn’t truncate and hides the icon.

Name the dropdown with Label, or with Prefix inside the trigger. Without either, set aria-label on the trigger and include the value.

The popup opens below the trigger, aligned to its start edge, and repositions when space is limited. Set alignItemWithTrigger on Positioner to overlap the trigger with the selected item instead.

Density

Use compact on the trigger in dense layouts, such as a filter beside a heading. The touch target keeps the default height, and on touch screens extends half the medium gap above and below. SearchableDropdown.Trigger takes the same density.

Code
density.tsx
import {Dropdown, Stack} from '@earendil-works/elanor-react';

const ranges = [
  {value: 'week', label: 'Last 7 days'},
  {value: 'month', label: 'Last 30 days'},
  {value: 'quarter', label: 'Last 90 days'},
];

export default function DropdownDensity() {
  return (
    <Stack direction="row" gap="large" align="end" wrap="wrap">
      {(['default', 'compact'] as const).map((density) => (
        <Dropdown.Root key={density} items={ranges} defaultValue="month">
          <Stack gap="small">
            <Dropdown.Label>{density === 'default' ? 'Default' : 'Compact'}</Dropdown.Label>
            <Dropdown.Trigger density={density}>
              <Dropdown.Value />
              <Dropdown.Icon />
            </Dropdown.Trigger>
          </Stack>
          <Dropdown.Portal>
            <Dropdown.Positioner>
              <Dropdown.Popup>
                <Dropdown.List>
                  {ranges.map((range) => (
                    <Dropdown.Item key={range.value} value={range.value}>
                      <Dropdown.ItemIndicator />
                      <Dropdown.ItemText>{range.label}</Dropdown.ItemText>
                    </Dropdown.Item>
                  ))}
                </Dropdown.List>
              </Dropdown.Popup>
            </Dropdown.Positioner>
          </Dropdown.Portal>
        </Dropdown.Root>
      ))}
    </Stack>
  );
}

Prefix

Prefix labels the dropdown inside the trigger, such as “Date range: Last 30 days”. Use it when there’s no room for Label, and keep it to a word or two. Prefix adds the colon, so leave it out of the text. Only the value truncates.

Use Prefix instead of Label, or instead of Field.Label inside a Field. It overrides aria-label on the trigger.

Code
prefix.tsx
import {Dropdown} from '@earendil-works/elanor-react';

const ranges = [
  {value: 'week', label: 'Last 7 days'},
  {value: 'month', label: 'Last 30 days'},
  {value: 'quarter', label: 'Last 90 days'},
];

export default function DropdownPrefix() {
  return (
    <Dropdown.Root items={ranges} defaultValue="month">
      <Dropdown.Trigger density="compact">
        <Dropdown.Prefix>Date range</Dropdown.Prefix>
        <Dropdown.Value />
        <Dropdown.Icon />
      </Dropdown.Trigger>
      <Dropdown.Portal>
        <Dropdown.Positioner>
          <Dropdown.Popup>
            <Dropdown.List>
              {ranges.map((range) => (
                <Dropdown.Item key={range.value} value={range.value}>
                  <Dropdown.ItemIndicator />
                  <Dropdown.ItemText>{range.label}</Dropdown.ItemText>
                </Dropdown.Item>
              ))}
            </Dropdown.List>
          </Dropdown.Popup>
        </Dropdown.Positioner>
      </Dropdown.Portal>
    </Dropdown.Root>
  );
}

Clear

Clear is a button after the trigger that clears the value and moves focus back to the trigger. It renders only while there’s something to clear. Put the trigger and Clear in Control, which joins them in one fill; Clear is the next tab stop after the trigger.

Use Clear on filters, where no value means no filter, not on view controls that always have a value, such as a date range or a sort order.

Name Clear with aria-label, such as “Clear Status filter”, and show the empty value with Value’s placeholder, such as “All”. Clear calls onValueChange with null, or [] with multiple, and the reason clear-press. A disabled or read-only Root, or a disabled Field or Fieldset, disables it. Its touch target matches the trigger’s.

When you control Root, pass null for no value, or [] with multiple, such as value={search.status ?? null}. undefined makes Root uncontrolled.

Code
clear.tsx
import {Dropdown, Grid} from '@earendil-works/elanor-react';

const statuses = [
  {value: 'active', label: 'Active'},
  {value: 'invited', label: 'Invited'},
  {value: 'suspended', label: 'Suspended'},
];

export default function DropdownClear() {
  return (
    <Grid.Root>
      <Grid.Item span={{base: 'full', xSmall: 4}}>
        <Dropdown.Root items={statuses} defaultValue="invited">
          <Dropdown.Control>
            <Dropdown.Trigger density="compact">
              <Dropdown.Prefix>Status</Dropdown.Prefix>
              <Dropdown.Value placeholder="All" />
              <Dropdown.Icon />
            </Dropdown.Trigger>
            <Dropdown.Clear aria-label="Clear Status filter" />
          </Dropdown.Control>
          <Dropdown.Portal>
            <Dropdown.Positioner>
              <Dropdown.Popup>
                <Dropdown.List>
                  {statuses.map((status) => (
                    <Dropdown.Item key={status.value} value={status.value}>
                      <Dropdown.ItemIndicator />
                      <Dropdown.ItemText>{status.label}</Dropdown.ItemText>
                    </Dropdown.Item>
                  ))}
                </Dropdown.List>
              </Dropdown.Popup>
            </Dropdown.Positioner>
          </Dropdown.Portal>
        </Dropdown.Root>
      </Grid.Item>
    </Grid.Root>
  );
}

With multiple, Clear empties the array.

Code
clear-multiple.tsx
import {Dropdown, Grid} from '@earendil-works/elanor-react';

const groups = ['developers', 'design', 'operations', 'support'];

export default function DropdownClearMultiple() {
  return (
    <Grid.Root>
      <Grid.Item span={{base: 'full', xSmall: 4}}>
        <Dropdown.Root multiple defaultValue={['developers', 'design', 'support']}>
          <Dropdown.Control>
            <Dropdown.Trigger density="compact">
              <Dropdown.Prefix>Groups</Dropdown.Prefix>
              <Dropdown.Value placeholder="All" />
              <Dropdown.Icon />
            </Dropdown.Trigger>
            <Dropdown.Clear aria-label="Clear Groups filter" />
          </Dropdown.Control>
          <Dropdown.Portal>
            <Dropdown.Positioner>
              <Dropdown.Popup>
                <Dropdown.List>
                  {groups.map((group) => (
                    <Dropdown.Item key={group} value={group}>
                      <Dropdown.ItemIndicator />
                      <Dropdown.ItemText>{group}</Dropdown.ItemText>
                    </Dropdown.Item>
                  ))}
                </Dropdown.List>
              </Dropdown.Popup>
            </Dropdown.Positioner>
          </Dropdown.Portal>
        </Dropdown.Root>
      </Grid.Item>
    </Grid.Root>
  );
}

Clear to a default

For a filter that starts at a value, such as API keys that show only active ones, pass the default to Root as clearValue. Clear then shows while the value differs from it, including when nothing is selected, and sets it back. Name it for what it does, such as “Reset Status filter to Active”.

Code
clear-default.tsx
import {Dropdown, Grid} from '@earendil-works/elanor-react';
import {useState} from 'react';

const statuses = [
  {value: 'active', label: 'Active'},
  {value: 'expired', label: 'Expired'},
  {value: 'revoked', label: 'Revoked'},
];
const defaultStatus = ['active'];

export default function DropdownClearDefault() {
  // Unchecking every status shows all keys; Clear goes back to the default.
  const [status, setStatus] = useState(defaultStatus);

  return (
    <Grid.Root>
      <Grid.Item span={{base: 'full', xSmall: 4}}>
        <Dropdown.Root
          multiple
          items={statuses}
          value={status}
          clearValue={defaultStatus}
          onValueChange={setStatus}
        >
          <Dropdown.Control>
            <Dropdown.Trigger density="compact">
              <Dropdown.Prefix>Status</Dropdown.Prefix>
              <Dropdown.Value placeholder="All" />
              <Dropdown.Icon />
            </Dropdown.Trigger>
            <Dropdown.Clear aria-label="Reset Status filter to Active" />
          </Dropdown.Control>
          <Dropdown.Portal>
            <Dropdown.Positioner>
              <Dropdown.Popup>
                <Dropdown.List>
                  {statuses.map((option) => (
                    <Dropdown.Item key={option.value} value={option.value}>
                      <Dropdown.ItemIndicator />
                      <Dropdown.ItemText>{option.label}</Dropdown.ItemText>
                    </Dropdown.Item>
                  ))}
                </Dropdown.List>
              </Dropdown.Popup>
            </Dropdown.Positioner>
          </Dropdown.Portal>
        </Dropdown.Root>
      </Grid.Item>
    </Grid.Root>
  );
}

Value count

ValueCount shows how many more values are selected, such as “Groups: developers +2”. Render the first selected value with Value’s children and put ValueCount after it. It counts from Root unless you pass count, and stays whole while the value truncates. Screen readers read “developers and 2 more”; pass label to translate it.

Code
value-count.tsx
import {Dropdown, Grid} from '@earendil-works/elanor-react';

const groups = ['developers', 'design', 'operations', 'support'];

export default function DropdownValueCount() {
  return (
    <Grid.Root>
      <Grid.Item span={{base: 'full', xSmall: 4}}>
        <Dropdown.Root multiple defaultValue={['developers', 'design', 'support']}>
          <Dropdown.Trigger density="compact">
            <Dropdown.Prefix>Groups</Dropdown.Prefix>
            <Dropdown.Value>{(values: string[]) => values[0] ?? 'All'}</Dropdown.Value>
            <Dropdown.ValueCount />
            <Dropdown.Icon />
          </Dropdown.Trigger>
          <Dropdown.Portal>
            <Dropdown.Positioner>
              <Dropdown.Popup>
                <Dropdown.List>
                  {groups.map((group) => (
                    <Dropdown.Item key={group} value={group}>
                      <Dropdown.ItemIndicator />
                      <Dropdown.ItemText>{group}</Dropdown.ItemText>
                    </Dropdown.Item>
                  ))}
                </Dropdown.List>
              </Dropdown.Popup>
            </Dropdown.Positioner>
          </Dropdown.Portal>
        </Dropdown.Root>
      </Grid.Item>
    </Grid.Root>
  );
}

Item indicator

ItemIndicator marks the selected item. Put the label next to it in ItemText.

Code
item-indicator.tsx
import {Dropdown, Stack} from '@earendil-works/elanor-react';

const projects = ['Website redesign', 'Design system', 'Release notes'];

export default function DropdownItemIndicator() {
  return (
    <Dropdown.Root defaultValue="Design system">
      <Stack gap="small">
        <Dropdown.Label>Project</Dropdown.Label>
        <Dropdown.Trigger>
          <Dropdown.Value />
          <Dropdown.Icon />
        </Dropdown.Trigger>
      </Stack>
      <Dropdown.Portal>
        <Dropdown.Positioner>
          <Dropdown.Popup>
            <Dropdown.List>
              {projects.map((project) => (
                <Dropdown.Item key={project} value={project}>
                  <Dropdown.ItemIndicator />
                  <Dropdown.ItemText>{project}</Dropdown.ItemText>
                </Dropdown.Item>
              ))}
            </Dropdown.List>
          </Dropdown.Popup>
        </Dropdown.Positioner>
      </Dropdown.Portal>
    </Dropdown.Root>
  );
}

Multiple selection

With multiple, the value is an array and ItemIndicator shows a checkbox.

Code
multiple.tsx
import {Dropdown, Stack} from '@earendil-works/elanor-react';

const projects = ['Website redesign', 'Design system', 'Release notes'];

export default function DropdownMultiple() {
  return (
    <Dropdown.Root multiple defaultValue={['Design system']}>
      <Stack gap="small">
        <Dropdown.Label>Projects</Dropdown.Label>
        <Dropdown.Trigger>
          <Dropdown.Value placeholder="Select projects" />
          <Dropdown.Icon />
        </Dropdown.Trigger>
      </Stack>
      <Dropdown.Portal>
        <Dropdown.Positioner>
          <Dropdown.Popup>
            <Dropdown.List>
              {projects.map((project) => (
                <Dropdown.Item key={project} value={project}>
                  <Dropdown.ItemIndicator />
                  <Dropdown.ItemText>{project}</Dropdown.ItemText>
                </Dropdown.Item>
              ))}
            </Dropdown.List>
          </Dropdown.Popup>
        </Dropdown.Positioner>
      </Dropdown.Portal>
    </Dropdown.Root>
  );
}

Groups

Group, GroupLabel, and Separator divide long lists.

Code
groups.tsx
import {Dropdown, Stack} from '@earendil-works/elanor-react';

const regions = {
  Europe: ['Frankfurt', 'London'],
  'North America': ['Toronto', 'Virginia'],
};

export default function DropdownGroups() {
  return (
    <Dropdown.Root defaultValue="Frankfurt">
      <Stack gap="small">
        <Dropdown.Label>Region</Dropdown.Label>
        <Dropdown.Trigger>
          <Dropdown.Value />
          <Dropdown.Icon />
        </Dropdown.Trigger>
      </Stack>
      <Dropdown.Portal>
        <Dropdown.Positioner>
          <Dropdown.Popup>
            <Dropdown.List>
              {Object.entries(regions).map(([continent, cities], index) => (
                <Dropdown.Group key={continent}>
                  {index > 0 ? <Dropdown.Separator /> : null}
                  <Dropdown.GroupLabel>{continent}</Dropdown.GroupLabel>
                  {cities.map((city) => (
                    <Dropdown.Item key={city} value={city}>
                      {city}
                    </Dropdown.Item>
                  ))}
                </Dropdown.Group>
              ))}
            </Dropdown.List>
          </Dropdown.Popup>
        </Dropdown.Positioner>
      </Dropdown.Portal>
    </Dropdown.Root>
  );
}

Searchable

SearchableDropdown adds a filter input to the popup, aligned like Dropdown’s. Keep Status outside the popup so screen readers announce the selection after it closes. For a list that is always visible, see Searchable list.

Project
Code
searchable.tsx
import {SearchableDropdown, Stack} from '@earendil-works/elanor-react';
import {useState} from 'react';

const projects = ['Website redesign', 'Design system', 'Release notes', 'Help center'];

export default function DropdownSearchable() {
  const [project, setProject] = useState<string | null>(null);

  return (
    <SearchableDropdown.Root items={projects} value={project} onValueChange={setProject}>
      <Stack gap="small">
        <SearchableDropdown.Label>Project</SearchableDropdown.Label>
        <SearchableDropdown.Trigger>
          <SearchableDropdown.Value placeholder="Select a project" />
          <SearchableDropdown.Icon />
        </SearchableDropdown.Trigger>
      </Stack>
      <SearchableDropdown.Portal>
        <SearchableDropdown.Positioner>
          <SearchableDropdown.Popup aria-label="Choose a project">
            <SearchableDropdown.InputGroup>
              <SearchableDropdown.Input
                aria-label="Filter projects"
                placeholder="Filter projects"
              />
            </SearchableDropdown.InputGroup>
            <SearchableDropdown.Empty>No projects found.</SearchableDropdown.Empty>
            <SearchableDropdown.List>
              {(item: string) => (
                <SearchableDropdown.Item key={item} value={item}>
                  <SearchableDropdown.ItemIndicator />
                  {item}
                </SearchableDropdown.Item>
              )}
            </SearchableDropdown.List>
          </SearchableDropdown.Popup>
        </SearchableDropdown.Positioner>
      </SearchableDropdown.Portal>
      <SearchableDropdown.Status>
        {project === null ? null : `Selected ${project}.`}
      </SearchableDropdown.Status>
    </SearchableDropdown.Root>
  );
}

SearchableDropdown has the same Prefix, ValueCount, Control and Clear. Its Value renders no element, so wrap it in a span to truncate it.

Code
searchable-filter.tsx
import {Grid, SearchableDropdown} from '@earendil-works/elanor-react';
import {useState} from 'react';

const groups = ['developers', 'design', 'operations', 'support', 'marketing', 'finance'];

export default function DropdownSearchableFilter() {
  const [selected, setSelected] = useState(['developers', 'operations']);

  return (
    <Grid.Root>
      <Grid.Item span={{base: 'full', xSmall: 4}}>
        <SearchableDropdown.Root
          items={groups}
          multiple
          value={selected}
          onValueChange={setSelected}
        >
          <SearchableDropdown.Control>
            <SearchableDropdown.Trigger density="compact">
              <SearchableDropdown.Prefix>Groups</SearchableDropdown.Prefix>
              <span>
                <SearchableDropdown.Value>
                  {(values: string[]) => values[0] ?? 'All'}
                </SearchableDropdown.Value>
              </span>
              <SearchableDropdown.ValueCount />
              <SearchableDropdown.Icon />
            </SearchableDropdown.Trigger>
            <SearchableDropdown.Clear aria-label="Clear Groups filter" />
          </SearchableDropdown.Control>
          <SearchableDropdown.Portal>
            <SearchableDropdown.Positioner>
              <SearchableDropdown.Popup aria-label="Choose groups">
                <SearchableDropdown.InputGroup>
                  <SearchableDropdown.Input
                    aria-label="Search groups"
                    placeholder="Search groups"
                  />
                </SearchableDropdown.InputGroup>
                <SearchableDropdown.Empty>No matching groups.</SearchableDropdown.Empty>
                <SearchableDropdown.List>
                  {(group: string) => (
                    <SearchableDropdown.Item key={group} value={group}>
                      <SearchableDropdown.ItemIndicator />
                      {group}
                    </SearchableDropdown.Item>
                  )}
                </SearchableDropdown.List>
              </SearchableDropdown.Popup>
            </SearchableDropdown.Positioner>
          </SearchableDropdown.Portal>
        </SearchableDropdown.Root>
      </Grid.Item>
    </Grid.Root>
  );
}

API

Dropdown.Root

PropTypeDefaultDescription
onValueChange| (( value: DropdownRootChangedValue<Value, Multiple>, eventDetails: DropdownRootChangeEventDetails, ) => void)-Called when the value changes. When Clear clears it, the value is clearValue and the reason is clear-press.
clearValueDropdownRootChangedValue<Value, Multiple>null, or [] with `multiple`The value Clear sets, such as a filter's default. Clear shows while the value differs from it. Compared as Base UI compares items, with isItemEqualToValue.

Also accepts Base UI Select.Root props.

Dropdown.Label

Accepts Base UI Select.Label props.

Dropdown.Trigger

PropTypeDefaultDescription
density'compact' | 'default''default'compact removes block padding but keeps the touch target height.

Also accepts Base UI Select.Trigger props.

Dropdown.Value

Accepts Base UI Select.Value props.

Dropdown.Icon

Accepts Base UI Select.Icon props.

Dropdown.Portal

Accepts Base UI Select.Portal props.

Dropdown.Positioner

PropTypeDefaultDescription
alignItemWithTriggerbooleanfalseWhether the positioner overlaps the trigger so the selected item's text is aligned with the trigger's value text.
alignAlign'start'How to align the popup relative to the specified side.

Also accepts Base UI Select.Positioner props.

Dropdown.Popup

Accepts Base UI Select.Popup props.

Dropdown.List

Accepts Base UI Select.List props.

Dropdown.Item

Accepts Base UI Select.Item props.

Dropdown.Prefix

A label inside the trigger, before the value. Use it instead of Label.

PropTypeDefaultDescription
onPointerDownPointerEventHandler<HTMLSpanElement>-A plain React handler; it replaces Base UI's label handler.

Also accepts Base UI Select props.

Dropdown.ItemIndicator

Accepts Base UI Select.ItemIndicator props.

Dropdown.GroupLabel

Accepts Base UI Select.GroupLabel props.

Dropdown.Separator

PropTypeDefaultDescription
orientation'horizontal'-Only horizontal is supported.

Also accepts Base UI Select.Separator props.

Dropdown.Control

Joins the trigger and Clear in one fill. data-has-value is set while a value is selected.

Renders <div>. Accepts its HTML attributes and ref.

Dropdown.Clear

Clears the value, to none or Root's clearValue, and focuses the trigger. Renders only while there's something to clear. Put it after the trigger, inside Control.

PropTypeDefaultDescription
aria-labelrequiredstring-Names the button, such as “Clear Status filter”.
type'submit' | 'reset' | 'button''button'

Also accepts Base UI Button props.

Dropdown.ValueCount

A muted count of the selected values after the first, such as “+2”.

PropTypeDefaultDescription
countnumberthe selected values after the firstHow many values to count, shown as “+2”. Nothing renders below 1.
label((count: number) => string)(count) => `and ${count} more`What screen readers read instead of “+2”, such as “and 2 more”.

Renders <span>. Also accepts its HTML attributes and ref.

Dropdown.ItemText

Accepts Base UI Select.ItemText props.

Dropdown.Group

Accepts Base UI Select.Group props.

SearchableDropdown.Root

PropTypeDefaultDescription
gridfalse-Only list navigation is supported; grid layouts are not.
clearValueSearchableDropdownRootChangedValue<Value, Multiple>null, or [] with `multiple`The value Clear sets, such as a filter's default. Clear shows while the value differs from it. Compared as Base UI compares items, with isItemEqualToValue.

Also accepts Base UI Combobox.Root props.

SearchableDropdown.Label

Accepts Base UI Combobox.Label props.

SearchableDropdown.Prefix

A label inside the trigger, before the value. Use it instead of Label.

PropTypeDefaultDescription
onPointerDownPointerEventHandler<HTMLSpanElement>-A plain React handler; it replaces Base UI's label handler.

Also accepts Base UI Combobox props.

SearchableDropdown.Trigger

PropTypeDefaultDescription
density'compact' | 'default''default'compact removes block padding but keeps the touch target height.

Also accepts Base UI Combobox.Trigger props.

SearchableDropdown.Icon

Accepts Base UI Combobox.Icon props.

SearchableDropdown.Positioner

Places the popup below the trigger, aligned to its start edge, as Dropdown's.

PropTypeDefaultDescription
alignAlign'start'How to align the popup relative to the specified side.

Also accepts Base UI Combobox.Positioner props.

SearchableDropdown.Popup

Accepts Base UI Combobox.Popup props.

SearchableDropdown.InputGroup

Accepts Base UI Combobox.InputGroup props.

SearchableDropdown.Input

PropTypeDefaultDescription
type'search' | 'text''text'

Also accepts Base UI Combobox.Input props.

SearchableDropdown.List

Accepts Base UI Combobox.List props.

SearchableDropdown.Item

Accepts Base UI Combobox.Item props.

SearchableDropdown.ItemIndicator

Accepts Base UI Combobox.ItemIndicator props.

SearchableDropdown.GroupLabel

Accepts Base UI Combobox.GroupLabel props.

SearchableDropdown.Separator

PropTypeDefaultDescription
orientation'horizontal'-Only horizontal is supported.

Also accepts Base UI Combobox.Separator props.

SearchableDropdown.Empty

Accepts Base UI Combobox.Empty props.

SearchableDropdown.Status

Accepts Base UI Combobox.Status props.

SearchableDropdown.Control

Joins the trigger and Clear in one fill. data-has-value is set while a value is selected.

Renders <div>. Accepts its HTML attributes and ref.

SearchableDropdown.Clear

Clears the value, to none or Root's clearValue, and focuses the trigger. Renders only while there's something to clear. Put it after the trigger, inside Control.

PropTypeDefaultDescription
aria-labelrequiredstring-Names the button, such as “Clear Status filter”.
type'submit' | 'reset' | 'button''button'

Also accepts Base UI Button props.

SearchableDropdown.ValueCount

A muted count of the selected values after the first, such as “+2”.

PropTypeDefaultDescription
countnumberthe selected values after the firstHow many values to count, shown as “+2”. Nothing renders below 1.
label((count: number) => string)(count) => `and ${count} more`What screen readers read instead of “+2”, such as “and 2 more”.

Renders <span>. Also accepts its HTML attributes and ref.

SearchableDropdown.Value

Accepts Base UI Combobox.Value props.

SearchableDropdown.Portal

Accepts Base UI Combobox.Portal props.

SearchableDropdown.Group

Accepts Base UI Combobox.Group props.

SearchableDropdown.Collection

Accepts Base UI Combobox.Collection props.