Dropdown
A single choice from a list the page draws itself. Use Select first: it is the default choice in a form and opens this same list. Reach for Dropdown for the Dropdown sets’ own trigger, or for a combo box or multi-select.
Live preview
Rendered by the component itself from the same generated tokens as the rest of the site.
const [value, setValue] = useState<string | null>(null)
<Dropdown
label="Theme"
options={options}
value={value}
onChange={setValue}
/>Installation
Install it with the shadcn CLI, along with everything it uses. New project? Set it up first, as Quick start shows.
npx shadcn@latest add https://www.graphite-ui.com/r/dropdown.jsonimport { ComboBox, Dropdown, MultiSelect } from '@/components/ui/dropdown'
import type { DropdownOption } from '@/components/ui/dropdown'Anatomy
The label, then the trigger: the chosen value (or the Prompt text) and a chevron, on surface-variant with a rule under it. Opened, the list hangs under the trigger at its width: rows at the trigger’s height, each with a rule 16 in at its top, the chosen one filled with primary-container and checked.
- LabelRequiredInput Label in on-surface-variant, above the trigger (Fixed), beside it (Inline) or inside it (Fluid). It names the combobox and its list.
- TriggerRequiredThe kit's Dropdown input. The value (or the Prompt text) in Body/3 on-surface, and a 16px chevron that turns over while open; in Error and Warning the status glyph comes before the chevron.
- ListRequiredThe kit's private _Dropdown menu list, under the trigger at its width, on surface with the overlay shadow, seven rows tall before it scrolls. Rows are the private _Dropdown list item at the trigger's height.
- Supporting textOptionalHelp, error or warning text under the trigger (beside it, Inline).
Variants
Style puts the label above (Fixed) or beside (Inline, which drops the trigger’s fill and rule). Fluid is a 64px box with the label inside. Size sets the trigger and the rows: 48, 40 or 32.
States
Focus and Open take the 2px focus ring. Error takes the danger ring and the status glyph before the chevron; Warning the warning glyph. Disabled takes the disabled fill. Read-only drops the fill and the chevron.
API reference
Generated from the contract, versioned with it, and checked by drift-check. This table cannot describe props the component does not have.
| Prop | Type | Default | Notes |
|---|---|---|---|
| kind | — | — | Three exports share this contract. The Dropdown is the select-only choice. The combo box form is the kit's Combo box (Default and Fluid), a single choice found by typing: the trigger is a text input that filters the list as the reader types (a label containing the text matches), with a 16px clear (cross-small) and a 1 × 16 outline divider before the chevron once a value is chosen. Only a listed option becomes the value; leaving the field puts the chosen label back. Its onChange takes null when cleared, and its Prompt text is "Filter...". The multi-select form is the kit's Multi-select and, with filterable, its Filterable multi-select: a list of values, each row leading with the checkbox glyph, the list staying open as rows are toggled. Its trigger counts the choice in a dismissible high-contrast Tag ("Clear all selected items") before the kit's Selected text ("Options selected"); filterable makes the trigger a text input that narrows the list, with a clear for what was typed. selectAll adds the kit's parent checkbox, an "All" row that is checked, mixed or clear. |
| options | — | — | Each a value and a label, optionally disabled. |
| value / onChange | — | — | Controlled. The chosen value, or null. |
| placeholder | — | — | The kit's Prompt text, "Choose an option" by default. |
| size | 'sm' | 'md' | 'lg' | — | The kit's Size, the trigger and the rows at 32, 40 or 48. Large by default. |
| layout | 'fixed' | 'inline' | 'fluid' | — | The kit's Style (Fixed, Inline) and its Dropdown - Fluid set. |
| helpText / errorText / warningText | — | — | Through the shared field-message rule. |
| disabled | boolean | — | |
| readOnly | boolean | — | Readable, not changeable; the chevron is hidden, and an empty value reads "No option selected", as the kit draws it. |
Design tokens
Every swatch is live. Change the source color in the header and this table repaints, because it reads the same roles the component does.
surface-variantThe trigger's fill, and a row under the pointer or the keyboard.outlineThe trigger's bottom rule, the rule 16 in at the top of each row, and the combo box's divider before the chevron.surfaceThe list's fill.on-surfaceThe value and Prompt text, the chevron, the active and selected rows' labels, the multi-select's checkbox glyph and the "All" row's label.on-surface-variantThe label, help text and the rows' labels at rest; the combo box's empty-field text and "No matches".primary-containerThe selected row's fill.primaryThe focus ring and the active row's border through primary-focus; the disabled fill and content through primary-disabled and primary-disabled-content.dangerThe error ring, the error text and the error glyph's triangle.warningThe warning glyph's triangle.on-warningThe "!" on the warning glyph.backgroundThe "!" on the error glyph.shadowThe list's overlay shadow.motionThe list's entrance and the chevron's turn.textBody/3 for the value and rows.spacingThe trigger's padding, the row height and padding, the rule's 16 inset, the Inline gaps.
Usage
The contract's prohibitions, written as the choices you will actually face.
Do
- Start with Select. Use Dropdown when the list must look like the kit’s.
- Keep option labels short enough for the trigger.
- Give it a label that says what is being chosen.
- Use Fluid inside a Fluid form, so it lines up with the fields around it.
Don’t
- Use it for actions. That is a Menu or a Menu button.
- Use it for two or three options a reader should see at once. That is a Radio button group.
- Use it for a long list without filtering. Use Combo box, so the reader can type to find it.
- Leave it unlabelled.
Accessibility
What the component does for you, and what it leaves to you.
- Roles
- A
comboboxthat opens alistboxofoptions. Focus stays on the combobox; the option the keyboard is on is itsaria-activedescendant. - Keyboard
- Arrow Down, Arrow Up, Enter or Space opens. Arrows move, Home and End go to the ends, Enter or Space chooses, Escape closes. Typing jumps to the next option that starts with what was typed.
- Names
- The combobox is named by its label and its value; the listbox by the label.
- Multi-select
- A listbox with
aria-multiselectable: Enter (or Space) toggles the row the keyboard is on and keeps the list open. The count Tag’s close is “Clear all selected items”; “All” reads as checked, mixed or clear. - Combo box
- An editable
comboboxwitharia-autocomplete="list": typing filters and opens the list, arrows move, Enter chooses, Escape closes (or clears what was typed). The clear button is “Clear selected item”; a filter that leaves nothing reads “No matches”. - Focus
- A 2px
--graphite-primary-focusring on the trigger, and inside the row the keyboard is on.
Figma parity
The kit’s Dropdown page has eight public sets, four kinds in Default and Fluid. This is the first kind, Dropdown; the other three extend the same contract as they land.
| Kit axis | Values | Code | How it maps |
|---|---|---|---|
| Set | Dropdown - Default · Fluid | layout | fixed and inline for Default, fluid for Fluid. |
| Style | Fixed · Inline | layout | Label above or beside. |
| Size | Large · Medium · Small | size | The trigger and the rows at 48, 40 and 32. |
| State | Enabled · Hover · Focus · Error · Warning · Disabled · Read-only | — | Hover draws nothing on the trigger in the kit; Focus and Open are the trigger’s; the rest are props. |
| State | Skeleton | — | No counterpart by rule. |
| Open · Selected | Boolean | value | The list showing, and a value chosen. |
| List item | Enabled · Hover · Selected · Selected + Hover · Disabled | options | Drawn from the active and selected option. |
| Trigger | surface-variant · outline | — | Not the field shell: all eight Dropdown sets draw it this way, so it is kept (rule 7). |
| Set | Combo box - Default · Fluid | ComboBox | Typed to filter; the clear and divider show once a value is chosen. The kit has no Inline Combo box. |
| Set | Multi-select - Default · Fluid | MultiSelect | Checkbox rows; the count Tag clears the choice. selectAll adds the parent checkbox. |
| Set | Filterable multi-select - Default · Fluid | MultiSelect filterable | Typed to filter. The kit defaults these sets to Hover, and draws the clear whenever something is chosen; here it clears the typed filter. |
| AI layer · AI label | Instances | — | Not built: the AI sets are ungoverned. |
