Aksara UI

DropdownMenu

A value picker with a button trigger and a list of options.

import { DropdownMenu } from '@aksornproject/aksara-ui'

For a labelled form field, use Select. To run actions, use Menu.

Sort picker

DropdownMenu has 11 parts. DropdownMenu.Root is Select.Root. DropdownMenu.Button is a library Button that shows leadingText, the selected value and a trailingNode that swaps between its open and closed nodes. DropdownMenu.List is the portal, positioner, popup and list in one part.

The popup is at least 230px wide and at most 256px tall, with a border and a 10px radius. A selected item shows a 24px accent tick. selectedIndicator replaces the tick, and an empty fragment hides it.

<DropdownMenu.Root items={options} value={sort}>
  <DropdownMenu.Button
    variant="secondary-accent"
    leadingText="Sort by:"
    trailingNode={{
      open: <IconInterfaceEssentialArrow1 className="ml-auto" />,
      closed: <IconInterfaceEssentialArrow className="ml-auto" />,
    }}
    className="w-[216px]"
  />
  <DropdownMenu.List align="end">
    {options.map((item) => (
      <DropdownMenu.Item
        key={item.value}
        value={item.value}
        onClick={() => setSort(item.value)}
      >
        {item.label}
      </DropdownMenu.Item>
    ))}
  </DropdownMenu.List>
</DropdownMenu.Root>

With Select

This is the same sort picker built with Select. Select.Trigger puts the label above the field, and the popup is at least 280px wide. For a list of actions, use Menu, whose items are announced as menuitem rather than option.

Sort by
<div className="w-[216px]">
  <Select.Root
    items={options}
    value={sort}
    onValueChange={(value) => value && setSort(value)}
  >
    <Select.Trigger label="Sort by" />
    <Select.Portal>
      <Select.Positioner align="end">
        <Select.Popup>
          <Select.List>
            {options.map((item) => (
              <Select.Item key={item.value} value={item.value}>
                {item.label}
              </Select.Item>
            ))}
          </Select.List>
        </Select.Popup>
      </Select.Positioner>
    </Select.Portal>
  </Select.Root>
</div>

Accessibility

  • DropdownMenu.Button is one native button with role="combobox". Its default type is button, so opening it inside a form does not submit the form.
  • A DropdownMenu.Trigger with a library Button child makes that button the trigger, so buttons do not nest.
  • The popup is a listbox of options. An action menu built on it announces values, not actions. Use Menu for actions.
  • Arrow keys reach disabled items, but Enter and clicks do not select them.

API

DropdownMenu.Root is Select.Root.

Prop

Type

A library Button, size="lg" by default, that takes every Button prop and these two. Its children are ignored. Its event handlers run before the Base UI handlers. disabled stops it opening.

PropTypeDefault
leadingTextstringrequired
trailingNode{ open: ReactNode; closed: ReactNode }none

Prop

Type

Takes the Base UI Select Popup props, children and these positioning props. className merges on the popup.

PropTypeDefault
sideSide'bottom'
sideOffsetnumber | OffsetFunction4
alignAlign'start'
alignOffsetnumber | OffsetFunction0
alignItemWithTriggerbooleanfalse

Takes the Base UI Select Item props. className merges with the defaults instead of replacing them.

PropTypeDefault
childrenReactNoderequired
selectedIndicatorReactNodethe 24px accent tick

Takes the Base UI Select Trigger props. A library Button child becomes the trigger. Other children render inside the Base UI trigger.

DropdownMenu.Value, Icon, Positioner, Popup, ItemIndicator and ItemText are the unstyled Base UI Select parts.

Prop

Type

Edit on GitHub

On this page