Dropdown

Use Dropdown component to choose option(s) from a list.
Import
JSX
import Dropdown from '@cronocode/react-box/components/dropdown';
Basic Dropdown
Choice
JSX
<Dropdown label="Choice" defaultValue={1}>
  <Dropdown.Item value={1}>Option 1</Dropdown.Item>
  <Dropdown.Item value={2}>Option 2</Dropdown.Item>
</Dropdown>

Keyboard and roles

This is the APG combobox, whole — in both of its shapes. The trigger is a role="combobox" that keeps DOM focus the entire time; the popup is a role="listbox" of role="option" rows, and the option the arrows are on is named by aria-activedescendant rather than focused. Add isSearchable and the combobox becomes the text field instead of a button. You write none of that — and if you were writing it by hand before, delete it.
Give it a label
A combobox is not named by what it contains: the text in the trigger is its value. Pass label and the component renders it and wires aria-labelledby — or pass your own aria-label in props. Without either, the control has no accessible name, exactly like an input with only a placeholder.
Clear and Select all are options too
Dropdown.Unselect and Dropdown.SelectAll sit inside the listbox, so they carry role="option" and the arrow keys reach them like any other row.
Searchable is the editable combobox
With isSearchable the text field is the combobox — the role, the ARIA and anything you pass in props live on the input, so nothing focusable sits inside anything else focusable. It is a different keyboard map, in the second table below: the printable keys type instead of navigating, and only Down and Up reach the listbox.

Select-only — the default

KeyWhat happens
Down / Up (closed)Opens, with the highlight on the selected option — or the first / last when nothing is chosen.
Alt + Down (closed)Opens without moving the highlight.
Enter / Space (closed)Opens. The browser own activation is suppressed, so it does not shut again.
Home / End (closed)Opens at the first or the last option.
A printable characterOpens with the first option starting with it already highlighted.
Down / Up (open)Moves the highlight, wrapping at the ends and skipping disabled options.
Home / End (open)Jumps to the first or last option.
Typing (open)Typeahead. A longer buffer narrows; the same letter twice cycles through the options sharing it.
Enter / Space (open)Chooses the highlighted option and closes. In multiple mode it toggles and stays open.
Alt + Up (open)Chooses the highlighted option and closes.
EscapeCloses, changing nothing. Focus never left the trigger, so nothing has to be restored.
Tab (open)Chooses the highlighted option, then moves on to the next control.

Editable — with isSearchable

KeyWhat happens
A printable characterTypes into the field, which opens the listbox and filters it. No typeahead — the field owns the keys.
Down / Up (closed)Opens, with the highlight on the selected option — or the first / last when nothing is chosen.
Alt + Down (closed)Opens without highlighting anything.
Down / Up (open)Moves the highlight through what the filter left, wrapping and skipping disabled options.
Home / End, Left / RightMove the caret, and hand the highlight back to the field — no option is where you are any more.
SpaceTypes a space. Only Enter chooses in this mode.
Enter (open)Chooses the highlighted option and puts its text in the field. With nothing highlighted it does nothing.
EscapeCloses the listbox, keeping what was typed. Pressed again on a closed one, it clears the field.
Tab (open)Chooses the highlighted option, then moves on to the next control.
Clicking awayCloses, and the field goes back to the value — a query left behind would describe a filter that is gone.
Controlled Dropdown
JSX
const [selectedValue, setSelectedValue] = useState<number>(2);

<Dropdown value={selectedValue} onChange={(value) => setSelectedValue(value!)}>
  <Dropdown.Item value={1}>Option 1</Dropdown.Item>
  <Dropdown.Item value={2}>Option 2</Dropdown.Item>
  <Dropdown.Item value={3}>Option 3</Dropdown.Item>
</Dropdown>
Unselect Item
JSX
<Dropdown>
  <Dropdown.Unselect>Select</Dropdown.Unselect>
  <Dropdown.Item value={1}>Option 1</Dropdown.Item>
  <Dropdown.Item value={2}>Option 2</Dropdown.Item>
</Dropdown>
Disabled
JSX
<Dropdown disabled width={50}>
  <DropdownUnselect>Select</DropdownUnselect>
  <DropdownItem value={1}>Option 1</DropdownItem>
  <DropdownItem value={2}>Option 2</DropdownItem>
</Dropdown>
Compact
JSX
<Dropdown variant="compact" width={40}>
  <DropdownUnselect>Select</DropdownUnselect>
  <DropdownItem value={1}>Option 1</DropdownItem>
  <DropdownItem value={2}>Option 2</DropdownItem>
</Dropdown>
Custom Variant (outlined)
JSX
// boxExtends.ts — register the variant, and export what you registered.
export const components = Box.components({
  dropdown: {
    variants: {
      outlined: {
        bgColor: 'transparent', b: 2, borderColor: 'indigo-500', color: 'indigo-600',
      },
    },
    children: {
      item: { variants: { outlined: { hover: { bgColor: 'indigo-50' }, selected: { bgColor: 'indigo-100' } } } },
      items: { variants: { outlined: { b: 2, borderColor: 'indigo-500' } } },
    },
  },
});

// box.d.ts — teach TypeScript the name, or variant="outlined" stays a type error.
import { ExtractComponentsAndVariants } from '@cronocode/react-box/types';
import { components } from './boxExtends';

declare module '@cronocode/react-box/types' {
  namespace Augmented {
    interface ComponentsTypes extends ExtractComponentsAndVariants<typeof components> {}
  }
}

// Use — the variant propagates to all children automatically.
<Dropdown variant="outlined">
  <Dropdown.Item value={1}>Option 1</Dropdown.Item>
  <Dropdown.Item value={2}>Option 2</Dropdown.Item>
</Dropdown>
Searchable
User
Select
JSX
<Dropdown
  label="User"
  isSearchable
  searchPlaceholder="Search users..."
  width={50}
>
  <DropdownUnselect>Select</DropdownUnselect>
  <DropdownItem value={1}>John Doe</DropdownItem>
  <DropdownItem value={2}>Joe Smith</DropdownItem>
  <DropdownItem value={3}>Alice</DropdownItem>
  <DropdownItem value={4}>Bob</DropdownItem>
</Dropdown>
Searchable with Empty Item
User
Select
JSX
<Dropdown
  label="User"
  isSearchable
  searchPlaceholder="Search users..."
  width={50}
>
  <DropdownEmptyItem>No options</DropdownEmptyItem>
  <DropdownUnselect>Select</DropdownUnselect>
  <DropdownItem value={1}>John Doe</DropdownItem>
  <DropdownItem value={2}>Joe Smith</DropdownItem>
  <DropdownItem value={3}>Alice</DropdownItem>
  <DropdownItem value={4}>Bob</DropdownItem>
</Dropdown>
Multiple Selection
JSX
<Dropdown multiple width={50}>
  <DropdownEmptyItem>No options</DropdownEmptyItem>
  <DropdownUnselect>Unselect All</DropdownUnselect>
  <DropdownSelectAll>Select All</DropdownSelectAll>
  <DropdownItem value={1}>John Doe</DropdownItem>
  <DropdownItem value={2}>Joe Smith</DropdownItem>
  <DropdownItem value={3}>Alice</DropdownItem>
  <DropdownItem value={4}>Bob</DropdownItem>
</Dropdown>
Multiple Selection with Checkboxes
JSX
<Dropdown multiple showCheckbox width={50}>
  <DropdownEmptyItem>No options</DropdownEmptyItem>
  <DropdownUnselect>Unselect All</DropdownUnselect>
  <DropdownSelectAll>Select All</DropdownSelectAll>
  <DropdownItem value={1}>John Doe</DropdownItem>
  <DropdownItem value={2}>Joe Smith</DropdownItem>
  <DropdownItem value={3}>Alice</DropdownItem>
  <DropdownItem value={4}>Bob</DropdownItem>
</Dropdown>
Multiple with Custom Display
JSX
<Dropdown multiple showCheckbox width={50}>
  <DropdownDisplay />
  <DropdownEmptyItem>No options</DropdownEmptyItem>
  <DropdownUnselect>Unselect All</DropdownUnselect>
  <DropdownSelectAll>Select All</DropdownSelectAll>
  <DropdownItem value={1}>John Doe</DropdownItem>
  <DropdownItem value={2}>Joe Smith</DropdownItem>
  <DropdownItem value={3}>Alice</DropdownItem>
  <DropdownItem value={4}>Bob</DropdownItem>
</Dropdown>

Select

Data-driven dropdown — pass data + def instead of composing children. Wraps Dropdown internally.
Import
JSX
import Select from '@cronocode/react-box/components/select';
Basic Select
User
JSX
const users = [
  { id: 1, name: 'John Doe', role: 'Admin' },
  { id: 2, name: 'Joe Smith', role: 'Editor' },
  { id: 3, name: 'Alice Brown', role: 'Viewer' },
];

<Select
  label="User"
  data={users}
  def={{ valueKey: 'id', displayKey: 'name', placeholder: 'Pick a user...' }}
  width={50}
/>
Custom Item Display
JSX
<Select
  data={users}
  def={{
    valueKey: 'id',
    display: (user) => `${user.name}${user.role}`,
    placeholder: 'Pick a user...',
  }}
/>
Multiple with Search
Users
Pick users...
JSX
<Select
  data={users}
  def={{
    valueKey: 'id',
    displayKey: 'name',
    placeholder: 'Pick users...',
    selectAllText: 'Select all',
    emptyText: 'No users found',
    selectedDisplay: (rows) =>
      rows.length === 0 ? 'Pick users...' : `${rows.length} selected`,
  }}
  label="Users" multiple showCheckbox isSearchable searchPlaceholder="Search users..."
/>